Compare commits

..

No commits in common. "955ff32dcd677dd57bfabdd04a99af29d94bd216" and "485dad01b70823f2e562ee0da14274785c53d2ea" have entirely different histories.

7 changed files with 134 additions and 88 deletions

View File

@ -1,18 +1,18 @@
codecov: codecov:
require_ci_to_pass: true require_ci_to_pass: true
# https://docs.codecov.com/docs/components # https://docs.codecov.com/docs/flags#recommended-automatic-flag-management
component_management: # Require each flag to have 1 upload before notification
individual_components: flag_management:
- component_id: backend individual_flags:
- name: backend
paths: paths:
- src/** - src/
- component_id: frontend - name: frontend
paths: paths:
- src-ui/** - src-ui/
# https://docs.codecov.com/docs/pull-request-comments # https://docs.codecov.com/docs/pull-request-comments
# codecov will only comment if coverage changes # codecov will only comment if coverage changes
comment: comment:
layout: "header, diff, components, flags, files"
require_changes: true require_changes: true
# https://docs.codecov.com/docs/javascript-bundle-analysis # https://docs.codecov.com/docs/javascript-bundle-analysis
require_bundle_changes: true require_bundle_changes: true

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
github: [shamoon, stumpylog]

View File

@ -162,20 +162,16 @@ jobs:
--frozen \ --frozen \
pytest pytest
- -
name: Upload backend test results to Codecov name: Upload coverage
if: always() if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
uses: codecov/test-results-action@v1 uses: actions/upload-artifact@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} name: backend-coverage-report
flags: backend-python-${{ matrix.python-version }} path: |
files: junit.xml coverage.xml
- junit.xml
name: Upload backend coverage to Codecov retention-days: 7
uses: codecov/codecov-action@v5 if-no-files-found: error
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: backend-python-${{ matrix.python-version }}
files: coverage.xml
- -
name: Stop containers name: Stop containers
if: always() if: always()
@ -183,7 +179,7 @@ jobs:
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
install-frontend-dependencies: install-frontend-depedendencies:
name: "Install Frontend Dependencies" name: "Install Frontend Dependencies"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
@ -218,7 +214,7 @@ jobs:
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})" name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- install-frontend-dependencies - install-frontend-depedendencies
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -249,33 +245,111 @@ jobs:
run: cd src-ui && npm run lint run: cd src-ui && npm run lint
- -
name: Run Jest unit tests name: Run Jest unit tests
env:
JEST_JUNIT_OUTPUT_FILE: junit-report-${{ matrix.shard-index }}.xml
run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
-
name: Upload Jest coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: jest-coverage-report-${{ matrix.shard-index }}
path: |
src-ui/coverage/coverage-final.json
src-ui/coverage/lcov.info
src-ui/coverage/clover.xml
retention-days: 7
if-no-files-found: error
- -
name: Run Playwright e2e tests name: Run Playwright e2e tests
run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
- -
name: Upload frontend test results to Codecov name: Upload Playwright test results
uses: codecov/test-results-action@v1
if: always() if: always()
uses: actions/upload-artifact@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} name: playwright-report-${{ matrix.shard-index }}
flags: frontend-node-${{ matrix.node-version }} path: src-ui/playwright-report
directory: src-ui/ retention-days: 7
-
name: Upload frontend test results
if: always()
uses: actions/upload-artifact@v4
with:
name: junit-report-${{ matrix.shard-index }}
path: src-ui/junit-report-${{ matrix.shard-index }}.xml
retention-days: 7
tests-coverage-upload:
name: "Upload to Codecov"
runs-on: ubuntu-24.04
needs:
- tests-backend
- tests-frontend
steps:
-
uses: actions/checkout@v4
-
name: Download frontend jest coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: jest-coverage-report-*
-
name: Download frontend playwright coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: playwright-report-*
merge-multiple: true
-
name: Download frontend test results
uses: actions/download-artifact@v4
with:
path: src-ui/junit/
pattern: junit-report-*
merge-multiple: true
- -
name: Upload frontend coverage to Codecov name: Upload frontend coverage to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
flags: frontend-node-${{ matrix.node-version }} flags: frontend
directory: src-ui/coverage/ directory: src-ui/coverage/
# dont include backend coverage files here
frontend-bundle-analysis: files: '!coverage.xml'
name: "Frontend Bundle Analysis" -
runs-on: ubuntu-24.04 name: Upload frontend test results to Codecov
needs: if: ${{ !cancelled() }}
- tests-frontend uses: codecov/test-results-action@v1
steps: with:
- uses: actions/checkout@v4 token: ${{ secrets.CODECOV_TOKEN }}
flags: frontend
directory: src-ui/junit/
-
name: Download backend coverage
uses: actions/download-artifact@v4
with:
name: backend-coverage-report
path: src/
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }}
# future expansion
flags: backend
directory: src/
-
name: Upload backend test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: backend
directory: src/
- -
name: Use Node.js 20 name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4

View File

@ -36,13 +36,7 @@ export const routes: Routes = [
component: AppFrameComponent, component: AppFrameComponent,
canDeactivate: [DirtyDocGuard], canDeactivate: [DirtyDocGuard],
children: [ children: [
{ { path: 'dashboard', component: DashboardComponent },
path: 'dashboard',
component: DashboardComponent,
data: {
componentName: 'AppFrameComponent',
},
},
{ {
path: 'documents', path: 'documents',
component: DocumentListComponent, component: DocumentListComponent,
@ -53,7 +47,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentListComponent',
}, },
}, },
{ {
@ -66,7 +59,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.SavedView, type: PermissionType.SavedView,
}, },
componentName: 'DocumentListComponent',
}, },
}, },
{ {
@ -78,7 +70,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentDetailComponent',
}, },
}, },
{ {
@ -90,7 +81,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentDetailComponent',
}, },
}, },
{ {
@ -102,7 +92,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'DocumentAsnComponent',
}, },
}, },
{ {
@ -114,7 +103,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Tag, type: PermissionType.Tag,
}, },
componentName: 'TagListComponent',
}, },
}, },
{ {
@ -126,7 +114,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.DocumentType, type: PermissionType.DocumentType,
}, },
componentName: 'DocumentTypeListComponent',
}, },
}, },
{ {
@ -138,7 +125,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Correspondent, type: PermissionType.Correspondent,
}, },
componentName: 'CorrespondentListComponent',
}, },
}, },
{ {
@ -150,7 +136,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.StoragePath, type: PermissionType.StoragePath,
}, },
componentName: 'StoragePathListComponent',
}, },
}, },
{ {
@ -159,7 +144,6 @@ export const routes: Routes = [
canActivate: [PermissionsGuard], canActivate: [PermissionsGuard],
data: { data: {
requireAdmin: true, requireAdmin: true,
componentName: 'LogsComponent',
}, },
}, },
{ {
@ -171,7 +155,6 @@ export const routes: Routes = [
action: PermissionAction.Delete, action: PermissionAction.Delete,
type: PermissionType.Document, type: PermissionType.Document,
}, },
componentName: 'TrashComponent',
}, },
}, },
// redirect old paths // redirect old paths
@ -197,7 +180,6 @@ export const routes: Routes = [
action: PermissionAction.Change, action: PermissionAction.Change,
type: PermissionType.UISettings, type: PermissionType.UISettings,
}, },
componentName: 'SettingsComponent',
}, },
}, },
{ {
@ -210,7 +192,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.UISettings, type: PermissionType.UISettings,
}, },
componentName: 'SettingsComponent',
}, },
}, },
{ {
@ -222,7 +203,6 @@ export const routes: Routes = [
action: PermissionAction.Change, action: PermissionAction.Change,
type: PermissionType.AppConfig, type: PermissionType.AppConfig,
}, },
componentName: 'ConfigComponent',
}, },
}, },
{ {
@ -234,7 +214,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.PaperlessTask, type: PermissionType.PaperlessTask,
}, },
componentName: 'TasksComponent',
}, },
}, },
{ {
@ -246,7 +225,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.CustomField, type: PermissionType.CustomField,
}, },
componentName: 'CustomFieldsComponent',
}, },
}, },
{ {
@ -258,7 +236,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.Workflow, type: PermissionType.Workflow,
}, },
componentName: 'WorkflowsComponent',
}, },
}, },
{ {
@ -270,7 +247,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.MailAccount, type: PermissionType.MailAccount,
}, },
componentName: 'MailComponent',
}, },
}, },
{ {
@ -282,7 +258,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.User, type: PermissionType.User,
}, },
componentName: 'UsersAndGroupsComponent',
}, },
}, },
{ {
@ -294,7 +269,6 @@ export const routes: Routes = [
action: PermissionAction.View, action: PermissionAction.View,
type: PermissionType.SavedView, type: PermissionType.SavedView,
}, },
componentName: 'SavedViewsComponent',
}, },
}, },
], ],

View File

@ -29,7 +29,7 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url', url: 'test-url',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )
@ -41,13 +41,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )
@ -59,13 +59,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
data: { componentName: 'TestComponent1' }, component: { name: 'TestComponent1' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
data: { componentName: 'TestComponent2' }, component: { name: 'TestComponent2' },
} as any) } as any)
) )
@ -76,13 +76,13 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-1', url: 'test-url-1',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url-2', url: 'test-url-2',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )
@ -93,7 +93,7 @@ describe('ComponentRouterService', () => {
eventsSubject.next( eventsSubject.next(
new ActivationStart({ new ActivationStart({
url: 'test-url', url: 'test-url',
data: { componentName: 'TestComponent' }, component: { name: 'TestComponent' },
} as any) } as any)
) )

View File

@ -17,11 +17,11 @@ export class ComponentRouterService {
.subscribe((event: ActivationStart) => { .subscribe((event: ActivationStart) => {
if ( if (
this.componentHistory[this.componentHistory.length - 1] !== this.componentHistory[this.componentHistory.length - 1] !==
event.snapshot.data.componentName && event.snapshot.component.name &&
!EXCLUDE_COMPONENTS.includes(event.snapshot.data.componentName) !EXCLUDE_COMPONENTS.includes(event.snapshot.component.name)
) { ) {
this.history.push(event.snapshot.url.toString()) this.history.push(event.snapshot.url.toString())
this.componentHistory.push(event.snapshot.data.componentName) this.componentHistory.push(event.snapshot.component.name)
} else { } else {
// Update the URL of the current component in case the same component was loaded via a different URL // Update the URL of the current component in case the same component was loaded via a different URL
this.history[this.history.length - 1] = event.snapshot.url.toString() this.history[this.history.length - 1] = event.snapshot.url.toString()

View File

@ -32,7 +32,6 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from documents.index import DelayedQuery
from documents.permissions import PaperlessObjectPermissions from documents.permissions import PaperlessObjectPermissions
from paperless.filters import GroupFilterSet from paperless.filters import GroupFilterSet
from paperless.filters import UserFilterSet from paperless.filters import UserFilterSet
@ -67,15 +66,15 @@ class StandardPagination(PageNumberPagination):
) )
def get_all_result_ids(self): def get_all_result_ids(self):
query = self.page.paginator.object_list ids = []
if isinstance(query, DelayedQuery): if hasattr(self.page.paginator.object_list, "saved_results"):
results_page = self.page.paginator.object_list.saved_results[0]
if results_page is not None:
for i in range(len(results_page.results.docs())):
try: try:
ids = [ fields = results_page.results.fields(i)
query.searcher.ixreader.stored_fields( if "id" in fields:
doc_num, ids.append(fields["id"])
)["id"]
for doc_num in query.saved_results.get(0).results.docs()
]
except Exception: except Exception:
pass pass
else: else: