mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-21 19:34:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff0ce4d123 | ||
|
|
8b3665b32d | ||
|
|
9cce6d1b68 | ||
|
|
bb77e65d52 | ||
|
|
eb5bf53476 |
@@ -1,5 +1,6 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'node:path'
|
||||
import { mockFilterSelectionData } from '../mock-filter-selection-data'
|
||||
|
||||
const REQUESTS_HAR = path.join(__dirname, 'requests/api-settings.har')
|
||||
|
||||
@@ -7,6 +8,7 @@ test('should activate / deactivate save button when settings change', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/settings')
|
||||
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
|
||||
await page.getByLabel('Use system setting').click()
|
||||
@@ -16,6 +18,7 @@ test('should activate / deactivate save button when settings change', async ({
|
||||
|
||||
test('should warn on unsaved changes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/settings')
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByRole('link', { name: 'Dashboard' }).click()
|
||||
@@ -28,6 +31,7 @@ test('should warn on unsaved changes', async ({ page }) => {
|
||||
|
||||
test('should apply appearance changes when set', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/settings')
|
||||
await expect(page.locator('html')).toHaveAttribute('data-bs-theme', /auto/)
|
||||
await page.getByLabel('Use system setting').click()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'node:path'
|
||||
import { mockFilterSelectionData } from '../mock-filter-selection-data'
|
||||
|
||||
const REQUESTS_HAR1 = path.join(__dirname, 'requests/api-dashboard1.har')
|
||||
const REQUESTS_HAR2 = path.join(__dirname, 'requests/api-dashboard2.har')
|
||||
@@ -8,6 +9,7 @@ const REQUESTS_HAR4 = path.join(__dirname, 'requests/api-dashboard4.har')
|
||||
|
||||
test('dashboard inbox link', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await page.getByRole('link', { name: 'Documents in inbox' }).click()
|
||||
await expect(page).toHaveURL(/tags__id__in=9/)
|
||||
@@ -16,6 +18,7 @@ test('dashboard inbox link', async ({ page }) => {
|
||||
|
||||
test('dashboard total documents link', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await page.getByRole('link').filter({ hasText: 'Total documents' }).click()
|
||||
await expect(page).toHaveURL(/documents/)
|
||||
@@ -25,6 +28,7 @@ test('dashboard total documents link', async ({ page }) => {
|
||||
|
||||
test('dashboard saved view show all', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await page
|
||||
.locator('pngx-widget-frame')
|
||||
@@ -38,6 +42,7 @@ test('dashboard saved view show all', async ({ page }) => {
|
||||
|
||||
test('dashboard saved view document links', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR4, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await page
|
||||
.locator('pngx-widget-frame')
|
||||
@@ -51,6 +56,7 @@ test('dashboard saved view document links', async ({ page }) => {
|
||||
|
||||
test('test slim sidebar', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await page.locator('.sidebar-slim-toggler').click()
|
||||
await expect(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'node:path'
|
||||
import { mockFilterSelectionData } from '../mock-filter-selection-data'
|
||||
|
||||
const REQUESTS_HAR = path.join(__dirname, 'requests/api-document-detail.har')
|
||||
const REQUESTS_HAR2 = path.join(__dirname, 'requests/api-document-detail2.har')
|
||||
@@ -8,6 +9,7 @@ test('should activate / deactivate save button when changes are saved', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/')
|
||||
await page.waitForSelector('pngx-document-detail pngx-input-text:first-child')
|
||||
await expect(page.getByTitle('Storage path', { exact: true })).toHaveText(
|
||||
@@ -20,6 +22,7 @@ test('should activate / deactivate save button when changes are saved', async ({
|
||||
|
||||
test('should warn on unsaved changes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/')
|
||||
await expect(page.getByTitle('Correspondent', { exact: true })).toHaveText(
|
||||
/\w+/
|
||||
@@ -39,6 +42,7 @@ test('should warn on unsaved changes', async ({ page }) => {
|
||||
|
||||
test('should support tab direct navigation', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/details')
|
||||
await expect(page.getByRole('tab', { name: 'Details' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
@@ -68,6 +72,7 @@ test('should support tab direct navigation', async ({ page }) => {
|
||||
|
||||
test('should show a mobile preview', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/')
|
||||
await page.setViewportSize({ width: 400, height: 1000 })
|
||||
await expect(page.getByRole('tab', { name: 'Preview' })).toBeVisible()
|
||||
@@ -77,6 +82,7 @@ test('should show a mobile preview', async ({ page }) => {
|
||||
|
||||
test('should show a list of notes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/notes')
|
||||
await expect(page.locator('pngx-document-notes')).toBeVisible()
|
||||
await expect(
|
||||
@@ -89,6 +95,7 @@ test('should show a list of notes', async ({ page }) => {
|
||||
|
||||
test('should support quick filters', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents/175/details')
|
||||
await page
|
||||
.getByRole('button', { name: 'Filter documents with these Tags' })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'node:path'
|
||||
import { mockFilterSelectionData } from '../mock-filter-selection-data'
|
||||
|
||||
const REQUESTS_HAR1 = path.join(__dirname, 'requests/api-document-list1.har')
|
||||
const REQUESTS_HAR2 = path.join(__dirname, 'requests/api-document-list2.har')
|
||||
@@ -10,6 +11,7 @@ const REQUESTS_HAR6 = path.join(__dirname, 'requests/api-document-list6.har')
|
||||
|
||||
test('basic filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Tags' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Inbox' }).click()
|
||||
@@ -45,6 +47,7 @@ test('basic filtering', async ({ page }) => {
|
||||
|
||||
test('text filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('main').getByRole('combobox').click()
|
||||
await page.getByRole('main').getByRole('combobox').fill('test')
|
||||
@@ -81,6 +84,7 @@ test('text filtering', async ({ page }) => {
|
||||
|
||||
test('date filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Dates' }).click()
|
||||
await page.locator('.ng-arrow-wrapper').first().click()
|
||||
@@ -103,6 +107,7 @@ test('date filtering', async ({ page }) => {
|
||||
|
||||
test('sorting', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR4, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'ASN' }).click()
|
||||
@@ -141,6 +146,7 @@ test('sorting', async ({ page }) => {
|
||||
|
||||
test('change views', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR5, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
await page.locator('.btn-group > label').first().click()
|
||||
await expect(page.locator('pngx-document-list table')).toBeVisible()
|
||||
@@ -152,6 +158,7 @@ test('change views', async ({ page }) => {
|
||||
|
||||
test('bulk edit', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR6, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/documents')
|
||||
|
||||
await page.locator('pngx-document-card-small').nth(0).click()
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Page } from '@playwright/test'
|
||||
|
||||
const EMPTY_SELECTION_DATA = {
|
||||
selected_correspondents: [],
|
||||
selected_tags: [],
|
||||
selected_document_types: [],
|
||||
selected_storage_paths: [],
|
||||
selected_custom_fields: [],
|
||||
}
|
||||
|
||||
/**
|
||||
* The document list now fires a GET to filter_selection_data on every
|
||||
* non-search reload(), independent of and concurrent with the main list
|
||||
* request. It's not present in any of the recorded HAR fixtures, so with
|
||||
* `notFound: 'fallback'` it would otherwise fall through to the real
|
||||
* network (nothing listens there in e2e, since only the frontend dev
|
||||
* server is started) and fail every test that reloads the list.
|
||||
*
|
||||
* Playwright checks routes in reverse-registration order, so this must be
|
||||
* registered before a test's own page.routeFromHAR() call for the HAR
|
||||
* route's `notFound: 'fallback'` to defer back to this one.
|
||||
*/
|
||||
export async function mockFilterSelectionData(page: Page) {
|
||||
await page.route('**/api/documents/filter_selection_data/**', (route) =>
|
||||
route.fulfill({
|
||||
json: EMPTY_SELECTION_DATA,
|
||||
// The app calls the (cross-origin, from the e2e app's perspective)
|
||||
// backend at http://localhost:8000 while served from :4200, so a
|
||||
// fulfilled response needs the same CORS header the real backend
|
||||
// sends (and that recorded HAR responses already carry) or the
|
||||
// browser rejects it as a cross-origin failure.
|
||||
headers: { 'Access-Control-Allow-Origin': 'http://localhost:4200' },
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'node:path'
|
||||
import { mockFilterSelectionData } from '../mock-filter-selection-data'
|
||||
|
||||
const REQUESTS_HAR = path.join(__dirname, 'requests/api-global-permissions.har')
|
||||
|
||||
test('should not allow user to edit settings', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Settings' })).not.toBeAttached()
|
||||
await page.goto('/settings')
|
||||
@@ -15,6 +17,7 @@ test('should not allow user to edit settings', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view documents', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.locator('nav').getByRole('link', { name: 'Documents' })
|
||||
@@ -31,6 +34,7 @@ test('should not allow user to view documents', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view correspondents', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Attributes' })
|
||||
@@ -43,6 +47,7 @@ test('should not allow user to view correspondents', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view tags', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Attributes' })
|
||||
@@ -55,6 +60,7 @@ test('should not allow user to view tags', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view document types', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Attributes' })
|
||||
@@ -67,6 +73,7 @@ test('should not allow user to view document types', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view storage paths', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Attributes' })
|
||||
@@ -79,6 +86,7 @@ test('should not allow user to view storage paths', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view logs', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Logs' })).not.toBeAttached()
|
||||
await page.goto('/logs')
|
||||
@@ -89,6 +97,7 @@ test('should not allow user to view logs', async ({ page }) => {
|
||||
|
||||
test('should not allow user to view tasks', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await mockFilterSelectionData(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Tasks' })).not.toBeAttached()
|
||||
await page.goto('/tasks')
|
||||
|
||||
@@ -191,6 +191,14 @@ describe('BulkEditorComponent', () => {
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// A filter_selection_data request now fires concurrently with every
|
||||
// non-search reload(), independent of whether a given test flushes or
|
||||
// even inspects the primary list response. Drain any left unclaimed.
|
||||
httpTestingController.match(
|
||||
(request) =>
|
||||
request.url ===
|
||||
`${environment.apiBaseUrl}documents/filter_selection_data/`
|
||||
)
|
||||
httpTestingController.verify()
|
||||
})
|
||||
|
||||
@@ -386,7 +394,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { add_tags: [101], remove_tags: [] },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -432,7 +440,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { add_tags: [101], remove_tags: [] },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
})
|
||||
|
||||
@@ -461,7 +469,7 @@ describe('BulkEditorComponent', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}documents/bulk_edit/`)
|
||||
.flush(true)
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -552,7 +560,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { correspondent: 101 },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -584,7 +592,7 @@ describe('BulkEditorComponent', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}documents/bulk_edit/`)
|
||||
.flush(true)
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -650,7 +658,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { document_type: 101 },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -682,7 +690,7 @@ describe('BulkEditorComponent', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}documents/bulk_edit/`)
|
||||
.flush(true)
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -748,7 +756,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { storage_path: 101 },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -780,7 +788,7 @@ describe('BulkEditorComponent', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}documents/bulk_edit/`)
|
||||
.flush(true)
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -846,7 +854,7 @@ describe('BulkEditorComponent', () => {
|
||||
parameters: { add_custom_fields: [101], remove_custom_fields: [102] },
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -878,7 +886,7 @@ describe('BulkEditorComponent', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}documents/bulk_edit/`)
|
||||
.flush(true)
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -987,7 +995,7 @@ describe('BulkEditorComponent', () => {
|
||||
documents: [3, 4],
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1080,7 +1088,7 @@ describe('BulkEditorComponent', () => {
|
||||
documents: [3, 4],
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1115,7 +1123,7 @@ describe('BulkEditorComponent', () => {
|
||||
source_mode: 'latest_version',
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1156,7 +1164,7 @@ describe('BulkEditorComponent', () => {
|
||||
metadata_document_id: 3,
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1175,7 +1183,7 @@ describe('BulkEditorComponent', () => {
|
||||
delete_originals: true,
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1196,7 +1204,7 @@ describe('BulkEditorComponent', () => {
|
||||
archive_fallback: true,
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1299,7 +1307,7 @@ describe('BulkEditorComponent', () => {
|
||||
},
|
||||
})
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
@@ -1607,7 +1615,7 @@ describe('BulkEditorComponent', () => {
|
||||
expect(toastServiceShowInfoSpy).toHaveBeenCalled()
|
||||
expect(listReloadSpy).toHaveBeenCalled()
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
) // list reload
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=100000&fields=id`
|
||||
|
||||
@@ -84,6 +84,28 @@ const view: SavedView = {
|
||||
filter_rules: filterRules,
|
||||
}
|
||||
|
||||
const emptySelectionData = {
|
||||
selected_correspondents: [],
|
||||
selected_tags: [],
|
||||
selected_document_types: [],
|
||||
selected_storage_paths: [],
|
||||
selected_custom_fields: [],
|
||||
}
|
||||
|
||||
// A successful (non-search) list response now triggers a separate,
|
||||
// non-blocking request for filter dropdown counts. Tests that flush a
|
||||
// successful list response need to also flush this follow-up request.
|
||||
function flushSelectionDataRequest(
|
||||
httpTestingController: HttpTestingController,
|
||||
querySuffix: string = ''
|
||||
) {
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/filter_selection_data/${querySuffix}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(emptySelectionData)
|
||||
}
|
||||
|
||||
describe('DocumentListViewService', () => {
|
||||
let httpTestingController: HttpTestingController
|
||||
let documentListViewService: DocumentListViewService
|
||||
@@ -105,6 +127,7 @@ describe('DocumentListViewService', () => {
|
||||
})
|
||||
|
||||
sessionStorage.clear()
|
||||
localStorage.clear()
|
||||
httpTestingController = TestBed.inject(HttpTestingController)
|
||||
documentListViewService = TestBed.inject(DocumentListViewService)
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
@@ -114,8 +137,19 @@ describe('DocumentListViewService', () => {
|
||||
|
||||
afterEach(() => {
|
||||
documentListViewService.cancelPending()
|
||||
// A filter_selection_data request now fires concurrently with every
|
||||
// non-search reload(), independent of whether the test cares about or
|
||||
// flushes the primary list response. Drain any that a test didn't
|
||||
// explicitly claim via flushSelectionDataRequest, so unrelated tests
|
||||
// don't have to know about this follow-up request to pass verify().
|
||||
httpTestingController.match(
|
||||
(request) =>
|
||||
request.url ===
|
||||
`${environment.apiBaseUrl}documents/filter_selection_data/`
|
||||
)
|
||||
httpTestingController.verify()
|
||||
sessionStorage.clear()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
@@ -128,10 +162,11 @@ describe('DocumentListViewService', () => {
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
documentListViewService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.isReloading).toBeFalsy()
|
||||
expect(documentListViewService.activeSavedViewId).toBeNull()
|
||||
@@ -143,12 +178,12 @@ describe('DocumentListViewService', () => {
|
||||
it('should handle error on page request out of range', () => {
|
||||
documentListViewService.currentPage = 50
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=50&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=50&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush([], { status: 404, statusText: 'Unexpected error' })
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
@@ -165,21 +200,20 @@ describe('DocumentListViewService', () => {
|
||||
]
|
||||
documentListViewService.setFilterRules(filterRulesAny)
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__in=${tags__id__in}`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__in=${tags__id__in}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(
|
||||
{ archive_serial_number: 'hello' },
|
||||
{ status: 404, statusText: 'Unexpected error' }
|
||||
)
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
// the error is a plain field error (not a page-out-of-range or deleted
|
||||
// custom-field-sort case), so no automatic retry request is sent here
|
||||
expect(documentListViewService.error).toBeTruthy()
|
||||
// reset the list
|
||||
documentListViewService.setFilterRules([])
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -187,7 +221,7 @@ describe('DocumentListViewService', () => {
|
||||
documentListViewService.currentPage = 1
|
||||
documentListViewService.sortField = 'custom_field_999'
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-custom_field_999&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-custom_field_999&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(
|
||||
@@ -196,7 +230,7 @@ describe('DocumentListViewService', () => {
|
||||
)
|
||||
// resets itself
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -211,7 +245,7 @@ describe('DocumentListViewService', () => {
|
||||
]
|
||||
documentListViewService.setFilterRules(filterRulesAny)
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__in=${tags__id__in}`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__in=${tags__id__in}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush('Generic error', { status: 404, statusText: 'Unexpected error' })
|
||||
@@ -219,7 +253,7 @@ describe('DocumentListViewService', () => {
|
||||
// reset the list
|
||||
documentListViewService.setFilterRules([])
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -228,7 +262,7 @@ describe('DocumentListViewService', () => {
|
||||
expect(documentListViewService.sortReverse).toBeTruthy()
|
||||
documentListViewService.setSort('added', false)
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=added&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=added&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.sortField).toEqual('added')
|
||||
@@ -236,12 +270,12 @@ describe('DocumentListViewService', () => {
|
||||
|
||||
documentListViewService.sortField = 'created'
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(documentListViewService.sortField).toEqual('created')
|
||||
documentListViewService.sortReverse = true
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.sortReverse).toBeTruthy()
|
||||
@@ -284,7 +318,7 @@ describe('DocumentListViewService', () => {
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=${page}&page_size=${
|
||||
documentListViewService.pageSize
|
||||
}&ordering=${reverse ? '-' : ''}${sort}&truncate_content=true&include_selection_data=true`
|
||||
}&ordering=${reverse ? '-' : ''}${sort}&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.currentPage).toEqual(page)
|
||||
@@ -301,7 +335,7 @@ describe('DocumentListViewService', () => {
|
||||
}
|
||||
documentListViewService.loadFromQueryParams(convertToParamMap(params))
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-added&truncate_content=true&include_selection_data=true&tags__id__all=${tags__id__all}`
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-added&truncate_content=true&include_selection_data=false&tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
expect(documentListViewService.filterRules).toEqual([
|
||||
@@ -311,12 +345,16 @@ describe('DocumentListViewService', () => {
|
||||
},
|
||||
])
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(
|
||||
httpTestingController,
|
||||
`?tags__id__all=${tags__id__all}`
|
||||
)
|
||||
})
|
||||
|
||||
it('should use filter rules to update query params', () => {
|
||||
documentListViewService.setFilterRules(filterRules)
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=${tags__id__all}`
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
})
|
||||
@@ -325,26 +363,31 @@ describe('DocumentListViewService', () => {
|
||||
documentListViewService.currentPage = 2
|
||||
let req = httpTestingController.expectOne((request) =>
|
||||
request.urlWithParams.startsWith(
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
|
||||
documentListViewService.setFilterRules(filterRules, true)
|
||||
|
||||
const filteredReqs = httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=${tags__id__all}`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(filteredReqs).toHaveLength(1)
|
||||
filteredReqs[0].flush(full_results)
|
||||
flushSelectionDataRequest(
|
||||
httpTestingController,
|
||||
`?tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
})
|
||||
|
||||
it('should support quick filter', () => {
|
||||
documentListViewService.quickFilter(filterRules)
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=${tags__id__all}`
|
||||
`${environment.apiBaseUrl}documents/?page=${documentListViewService.currentPage}&page_size=${documentListViewService.pageSize}&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
})
|
||||
@@ -367,21 +410,21 @@ describe('DocumentListViewService', () => {
|
||||
convertToParamMap(params)
|
||||
)
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=${page}&page_size=${documentListViewService.pageSize}&ordering=-added&truncate_content=true&include_selection_data=true&tags__id__all=${tags__id__all}`
|
||||
`${environment.apiBaseUrl}documents/?page=${page}&page_size=${documentListViewService.pageSize}&ordering=-added&truncate_content=true&include_selection_data=false&tags__id__all=${tags__id__all}`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
// reset the list
|
||||
documentListViewService.currentPage = 1
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-added&truncate_content=true&include_selection_data=true&tags__id__all=9`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-added&truncate_content=true&include_selection_data=false&tags__id__all=9`
|
||||
)
|
||||
documentListViewService.setFilterRules([])
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-added&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-added&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
documentListViewService.sortField = 'created'
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
documentListViewService.activateSavedView(null)
|
||||
})
|
||||
@@ -389,18 +432,22 @@ describe('DocumentListViewService', () => {
|
||||
it('should support navigating next / previous', () => {
|
||||
documentListViewService.setFilterRules([])
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
documentListViewService.pageSize = 3
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush({
|
||||
count: 3,
|
||||
results: documents.slice(0, 3),
|
||||
})
|
||||
// two reload()s ran above (setFilterRules, then pageSize), each firing
|
||||
// its own concurrent filter_selection_data request with an identical
|
||||
// (unfiltered) URL; this test doesn't assert on selectionData, so let
|
||||
// afterEach's drain step clean both up rather than disambiguating here.
|
||||
expect(documentListViewService.hasNext(documents[0].id)).toBeTruthy()
|
||||
expect(documentListViewService.hasPrevious(documents[0].id)).toBeFalsy()
|
||||
documentListViewService.getNext(documents[0].id).subscribe((docId) => {
|
||||
@@ -447,7 +494,7 @@ describe('DocumentListViewService', () => {
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
documentListViewService.pageSize = 3
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
jest
|
||||
.spyOn(documentListViewService, 'getLastPage')
|
||||
@@ -462,7 +509,7 @@ describe('DocumentListViewService', () => {
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
expect(documentListViewService.currentPage).toEqual(2)
|
||||
const reqs = httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(reqs.length).toBeGreaterThan(0)
|
||||
})
|
||||
@@ -497,11 +544,11 @@ describe('DocumentListViewService', () => {
|
||||
.mockReturnValue(documents)
|
||||
documentListViewService.currentPage = 2
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
documentListViewService.pageSize = 3
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=2&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
const reloadSpy = jest.spyOn(documentListViewService, 'reload')
|
||||
documentListViewService.getPrevious(1).subscribe({
|
||||
@@ -511,7 +558,7 @@ describe('DocumentListViewService', () => {
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
expect(documentListViewService.currentPage).toEqual(1)
|
||||
const reqs = httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(reqs.length).toBeGreaterThan(0)
|
||||
})
|
||||
@@ -524,10 +571,11 @@ describe('DocumentListViewService', () => {
|
||||
it('should support select a document', () => {
|
||||
documentListViewService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
documentListViewService.toggleSelected(documents[0])
|
||||
expect(documentListViewService.isSelected(documents[0])).toBeTruthy()
|
||||
documentListViewService.toggleSelected(documents[0])
|
||||
@@ -537,10 +585,11 @@ describe('DocumentListViewService', () => {
|
||||
it('should support select all', () => {
|
||||
documentListViewService.reload()
|
||||
const reloadReq = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(reloadReq.request.method).toEqual('GET')
|
||||
reloadReq.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
expect(documentListViewService.allSelected).toBeTruthy()
|
||||
@@ -553,13 +602,14 @@ describe('DocumentListViewService', () => {
|
||||
it('should support select page', () => {
|
||||
documentListViewService.pageSize = 3
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=3&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush({
|
||||
count: 3,
|
||||
results: documents.slice(0, 3),
|
||||
})
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
documentListViewService.selectPage()
|
||||
expect(documentListViewService.selected.size).toEqual(3)
|
||||
expect(documentListViewService.isSelected(documents[5])).toBeFalsy()
|
||||
@@ -568,10 +618,11 @@ describe('DocumentListViewService', () => {
|
||||
it('should support select range', () => {
|
||||
documentListViewService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
documentListViewService.toggleSelected(documents[0])
|
||||
expect(documentListViewService.isSelected(documents[0])).toBeTruthy()
|
||||
documentListViewService.selectRangeTo(documents[2])
|
||||
@@ -583,9 +634,10 @@ describe('DocumentListViewService', () => {
|
||||
it('should clear all-selected mode when toggling a single document', () => {
|
||||
documentListViewService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
expect(documentListViewService.allSelected).toBeTruthy()
|
||||
@@ -599,9 +651,10 @@ describe('DocumentListViewService', () => {
|
||||
it('should clear all-selected mode when selecting a range', () => {
|
||||
documentListViewService.reload()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
documentListViewService.toggleSelected(documents[1])
|
||||
@@ -619,22 +672,24 @@ describe('DocumentListViewService', () => {
|
||||
it('should support selection range reduction', () => {
|
||||
documentListViewService.reload()
|
||||
let req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush(full_results)
|
||||
flushSelectionDataRequest(httpTestingController)
|
||||
|
||||
documentListViewService.selectAll()
|
||||
expect(documentListViewService.selected.size).toEqual(6)
|
||||
|
||||
documentListViewService.setFilterRules(filterRules)
|
||||
req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=9`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false&tags__id__all=9`
|
||||
)
|
||||
req.flush({
|
||||
count: 3,
|
||||
results: documents.slice(0, 3),
|
||||
})
|
||||
flushSelectionDataRequest(httpTestingController, '?tags__id__all=9')
|
||||
expect(documentListViewService.allSelected).toBeTruthy()
|
||||
expect(documentListViewService.selected.size).toEqual(3)
|
||||
})
|
||||
@@ -643,7 +698,7 @@ describe('DocumentListViewService', () => {
|
||||
const cancelSpy = jest.spyOn(documentListViewService, 'cancelPending')
|
||||
documentListViewService.reload()
|
||||
httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true&tags__id__all=9`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(cancelSpy).toHaveBeenCalled()
|
||||
})
|
||||
@@ -662,7 +717,7 @@ describe('DocumentListViewService', () => {
|
||||
documentListViewService.setFilterRules([])
|
||||
expect(documentListViewService.sortField).toEqual('created')
|
||||
httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -689,11 +744,11 @@ describe('DocumentListViewService', () => {
|
||||
expect(localStorageSpy).toHaveBeenCalled()
|
||||
// reload triggered
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
documentListViewService.displayFields = null
|
||||
httpTestingController.match(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
expect(documentListViewService.displayFields).toEqual(
|
||||
DEFAULT_DISPLAY_FIELDS.filter((f) => f.id !== DisplayField.ADDED).map(
|
||||
@@ -738,7 +793,7 @@ describe('DocumentListViewService', () => {
|
||||
it('should generate quick filter URL preserving default state', () => {
|
||||
documentListViewService.reload()
|
||||
httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=true`
|
||||
`${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true&include_selection_data=false`
|
||||
)
|
||||
const urlTree = documentListViewService.getQuickFilterUrl(filterRules)
|
||||
expect(urlTree).toBeDefined()
|
||||
|
||||
@@ -314,12 +314,39 @@ export class DocumentListViewService {
|
||||
}
|
||||
}
|
||||
|
||||
private loadFilterSelectionData(filterRules: FilterRule[]) {
|
||||
this.documentService
|
||||
.getFilterSelectionData(filterRules)
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (selectionData) => {
|
||||
this.selectionData = selectionData
|
||||
this.markChanged()
|
||||
},
|
||||
error: () => {
|
||||
this.selectionData = null
|
||||
this.markChanged()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
reload(onFinish?, updateQueryParams: boolean = true) {
|
||||
this.cancelPending()
|
||||
this.isReloading = true
|
||||
this.error = null
|
||||
this.markChanged()
|
||||
let activeListViewState = this.activeListViewState
|
||||
// Full-text search results are already narrowed by the search backend, so
|
||||
// computing selection data inline there is cheap. A plain (unfiltered or
|
||||
// ORM-filtered) browse can span the entire document set, so its selection
|
||||
// data is fetched separately -- concurrently with the list itself, rather
|
||||
// than blocking or waiting on it.
|
||||
const isFullTextSearch = isFullTextFilterRule(
|
||||
activeListViewState.filterRules
|
||||
)
|
||||
if (!isFullTextSearch) {
|
||||
this.loadFilterSelectionData(activeListViewState.filterRules)
|
||||
}
|
||||
this.documentService
|
||||
.listFiltered(
|
||||
activeListViewState.currentPage,
|
||||
@@ -327,17 +354,22 @@ export class DocumentListViewService {
|
||||
activeListViewState.sortField,
|
||||
activeListViewState.sortReverse,
|
||||
activeListViewState.filterRules,
|
||||
{ truncate_content: true, include_selection_data: true }
|
||||
{
|
||||
truncate_content: true,
|
||||
include_selection_data: isFullTextSearch,
|
||||
}
|
||||
)
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
const resultWithSelectionData = result as DocumentResults
|
||||
this.initialized = true
|
||||
this.isReloading = false
|
||||
activeListViewState.collectionSize = result.count
|
||||
activeListViewState.documents = result.results
|
||||
this.selectionData = resultWithSelectionData.selection_data ?? null
|
||||
if (isFullTextSearch) {
|
||||
this.selectionData =
|
||||
(result as DocumentResults).selection_data ?? null
|
||||
}
|
||||
this.syncSelectedToCurrentPage()
|
||||
this.markChanged()
|
||||
|
||||
@@ -376,6 +408,9 @@ export class DocumentListViewService {
|
||||
// e.g. field was deleted
|
||||
this.sortField = 'created'
|
||||
} else {
|
||||
// cancel the concurrently-fired selection-data request too, so it
|
||||
// can't resolve afterward and clobber this reset with stale data
|
||||
this.cancelPending()
|
||||
this.selectionData = null
|
||||
let errorMessage
|
||||
if (
|
||||
|
||||
@@ -41,6 +41,24 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a plain params object into an HttpParams instance, skipping
|
||||
* null/undefined values so they aren't serialized as literal "null" /
|
||||
* "undefined" query string entries.
|
||||
*/
|
||||
protected withParams(
|
||||
params,
|
||||
base: HttpParams = new HttpParams()
|
||||
): HttpParams {
|
||||
let httpParams = base
|
||||
for (let key in params) {
|
||||
if (params[key] != null) {
|
||||
httpParams = httpParams.set(key, params[key])
|
||||
}
|
||||
}
|
||||
return httpParams
|
||||
}
|
||||
|
||||
list(
|
||||
page?: number,
|
||||
pageSize?: number,
|
||||
@@ -60,11 +78,7 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
if (ordering) {
|
||||
httpParams = httpParams.set('ordering', ordering)
|
||||
}
|
||||
for (let extraParamKey in extraParams) {
|
||||
if (extraParams[extraParamKey] != null) {
|
||||
httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
|
||||
}
|
||||
}
|
||||
httpParams = this.withParams(extraParams, httpParams)
|
||||
return this.http
|
||||
.get<Results<T>>(this.getResourceUrl(), {
|
||||
params: httpParams,
|
||||
@@ -113,11 +127,7 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
httpParams = httpParams.set('id__in', ids.join(','))
|
||||
httpParams = httpParams.set('ordering', '-id')
|
||||
httpParams = httpParams.set('page_size', 1000)
|
||||
for (let extraParamKey in extraParams) {
|
||||
if (extraParams[extraParamKey] != null) {
|
||||
httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
|
||||
}
|
||||
}
|
||||
httpParams = this.withParams(extraParams, httpParams)
|
||||
return this.http
|
||||
.get<Results<T>>(this.getResourceUrl(), {
|
||||
params: httpParams,
|
||||
|
||||
@@ -398,6 +398,13 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
)
|
||||
}
|
||||
|
||||
getFilterSelectionData(filterRules: FilterRule[]): Observable<SelectionData> {
|
||||
return this.http.get<SelectionData>(
|
||||
this.getResourceUrl(null, 'filter_selection_data'),
|
||||
{ params: this.withParams(queryParamsFromFilterRules(filterRules)) }
|
||||
)
|
||||
}
|
||||
|
||||
getSuggestions(id: number): Observable<DocumentSuggestions> {
|
||||
return this.http.get<DocumentSuggestions>(
|
||||
this.getResourceUrl(id, 'suggestions')
|
||||
|
||||
@@ -1241,7 +1241,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
],
|
||||
)
|
||||
|
||||
def test_list_with_include_selection_data(self) -> None:
|
||||
def test_selection_data_endpoint(self) -> None:
|
||||
correspondent = Correspondent.objects.create(name="c1")
|
||||
doc_type = DocumentType.objects.create(name="dt1")
|
||||
storage_path = StoragePath.objects.create(name="sp1")
|
||||
@@ -1259,30 +1259,28 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
non_matching_doc.tags.add(Tag.objects.create(name="other"))
|
||||
|
||||
response = self.client.get(
|
||||
f"/api/documents/?tags__id__in={tag.id}&include_selection_data=true",
|
||||
f"/api/documents/filter_selection_data/?tags__id__in={tag.id}",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertIn("selection_data", response.data)
|
||||
self.assertNotIn("results", response.data)
|
||||
|
||||
selected_correspondent = next(
|
||||
item
|
||||
for item in response.data["selection_data"]["selected_correspondents"]
|
||||
for item in response.data["selected_correspondents"]
|
||||
if item["id"] == correspondent.id
|
||||
)
|
||||
selected_tag = next(
|
||||
item
|
||||
for item in response.data["selection_data"]["selected_tags"]
|
||||
if item["id"] == tag.id
|
||||
item for item in response.data["selected_tags"] if item["id"] == tag.id
|
||||
)
|
||||
selected_type = next(
|
||||
item
|
||||
for item in response.data["selection_data"]["selected_document_types"]
|
||||
for item in response.data["selected_document_types"]
|
||||
if item["id"] == doc_type.id
|
||||
)
|
||||
selected_storage_path = next(
|
||||
item
|
||||
for item in response.data["selection_data"]["selected_storage_paths"]
|
||||
for item in response.data["selected_storage_paths"]
|
||||
if item["id"] == storage_path.id
|
||||
)
|
||||
|
||||
@@ -1291,6 +1289,17 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
self.assertEqual(selected_type["document_count"], 1)
|
||||
self.assertEqual(selected_storage_path["document_count"], 1)
|
||||
|
||||
def test_list_no_longer_supports_include_selection_data(self) -> None:
|
||||
"""
|
||||
include_selection_data was never part of a stable release (beta-only,
|
||||
introduced and removed within the 3.0.0-beta cycle) -- the plain list
|
||||
endpoint should just ignore the param now rather than compute it inline.
|
||||
"""
|
||||
response = self.client.get("/api/documents/?include_selection_data=true")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertNotIn("selection_data", response.data)
|
||||
|
||||
def test_statistics(self) -> None:
|
||||
doc1 = Document.objects.create(
|
||||
title="none1",
|
||||
|
||||
+25
-19
@@ -1034,7 +1034,11 @@ class DocumentViewSet(
|
||||
],
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
def _base_document_queryset(self):
|
||||
# Root documents only, with the annotations that filter_backends rely
|
||||
# on (effective_content for SearchFilter, num_notes for ordering) --
|
||||
# but no select_related/prefetch_related, since those only matter for
|
||||
# serializing documents, not for filtering, ordering, or aggregating.
|
||||
latest_version_content = Subquery(
|
||||
Document.objects.filter(root_document=OuterRef("pk"))
|
||||
.order_by("-id")
|
||||
@@ -1046,6 +1050,11 @@ class DocumentViewSet(
|
||||
.order_by("-created", "-id")
|
||||
.annotate(effective_content=Coalesce(latest_version_content, F("content")))
|
||||
.annotate(num_notes=Count("notes"))
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
self._base_document_queryset()
|
||||
.select_related("correspondent", "storage_path", "document_type", "owner")
|
||||
.prefetch_related(
|
||||
Prefetch(
|
||||
@@ -1184,24 +1193,21 @@ class DocumentViewSet(
|
||||
|
||||
return response
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
if not get_boolean(
|
||||
str(request.query_params.get("include_selection_data", "false")),
|
||||
):
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
selection_data = self._get_selection_data_for_queryset(queryset)
|
||||
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
response = self.get_paginated_response(serializer.data)
|
||||
response.data["selection_data"] = selection_data
|
||||
return response
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return Response({"results": serializer.data, "selection_data": selection_data})
|
||||
@extend_schema(
|
||||
operation_id="documents_filter_selection_data",
|
||||
description=(
|
||||
"Returns per-tag/correspondent/document-type/storage-path/custom-field "
|
||||
"document counts for the current filter, without paginating or "
|
||||
"serializing the matching documents themselves. Split out from the "
|
||||
"plain document list so that browsing the (potentially huge) unfiltered "
|
||||
"document list doesn't pay for this aggregation on every request."
|
||||
),
|
||||
responses={200: inline_serializer(name="SelectionData", fields={})},
|
||||
)
|
||||
@action(detail=False, methods=["get"], url_path="filter_selection_data")
|
||||
def filter_selection_data(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self._base_document_queryset())
|
||||
return Response(self._get_selection_data_for_queryset(queryset))
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
from documents.search import get_backend
|
||||
|
||||
Reference in New Issue
Block a user