]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Enhancement: document link field fixes (#5020)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Mon, 18 Dec 2023 00:39:45 +0000 (16:39 -0800)
committerGitHub <noreply@github.com>
Mon, 18 Dec 2023 00:39:45 +0000 (00:39 +0000)
* Implement more efficient getFew for document retrieval

* Filter out parent document ID & already-selected documents

* Clip very long document titles

src-ui/src/app/components/common/input/document-link/document-link.component.scss
src-ui/src/app/components/common/input/document-link/document-link.component.spec.ts
src-ui/src/app/components/common/input/document-link/document-link.component.ts
src-ui/src/app/components/document-detail/document-detail.component.html
src-ui/src/app/services/rest/abstract-paperless-service.spec.ts
src-ui/src/app/services/rest/abstract-paperless-service.ts

index bcaa4e849bf8da0e6a85e3827377bfe7b0c136f4..51f6fa05521571dd5875b5fd3f30e2c3fe5e47a4 100644 (file)
@@ -1,6 +1,10 @@
-::ng-deep .ng-select-container .ng-value-container .ng-value {
-    background-color: transparent !important;
-    border-color: transparent;
+::ng-deep .ng-select-container .ng-value-container {
+    overflow: hidden;
+
+    .ng-value {
+        background-color: transparent !important;
+        border-color: transparent;
+    }
 }
 
 .sidebaricon {
@@ -9,6 +13,4 @@
 
 .badge {
     font-size: .75rem;
-    // --bs-primary: var(--pngx-bg-alt);
-    // color: var(--pngx-primary-text-contrast);
 }
index d1af7ab2f86313437e1259996395cc7d0ee9a5f5..e00460ec55ae6b96274c44d810cf79f733fc4445 100644 (file)
@@ -20,6 +20,10 @@ const documents = [
     id: 12,
     title: 'Document 12 bar',
   },
+  {
+    id: 16,
+    title: 'Document 16 bar',
+  },
   {
     id: 23,
     title: 'Document 23 bar',
@@ -48,10 +52,15 @@ describe('DocumentLinkComponent', () => {
     fixture.detectChanges()
   })
 
-  it('should retrieve selected documents from APIs', () => {
-    const getSpy = jest.spyOn(documentService, 'getCachedMany')
+  it('should retrieve selected documents from API', () => {
+    const getSpy = jest.spyOn(documentService, 'getFew')
     getSpy.mockImplementation((ids) => {
-      return of(documents.filter((d) => ids.includes(d.id)))
+      const docs = documents.filter((d) => ids.includes(d.id))
+      return of({
+        count: docs.length,
+        all: docs.map((d) => d.id),
+        results: docs,
+      })
     })
     component.writeValue([1])
     expect(getSpy).toHaveBeenCalled()
@@ -85,12 +94,18 @@ describe('DocumentLinkComponent', () => {
   })
 
   it('should load values correctly', () => {
-    jest.spyOn(documentService, 'getCachedMany').mockImplementation((ids) => {
-      return of(documents.filter((d) => ids.includes(d.id)))
+    const getSpy = jest.spyOn(documentService, 'getFew')
+    getSpy.mockImplementation((ids) => {
+      const docs = documents.filter((d) => ids.includes(d.id))
+      return of({
+        count: docs.length,
+        all: docs.map((d) => d.id),
+        results: docs,
+      })
     })
     component.writeValue([12, 23])
     expect(component.value).toEqual([12, 23])
-    expect(component.selectedDocuments).toEqual([documents[1], documents[2]])
+    expect(component.selectedDocuments).toEqual([documents[1], documents[3]])
     component.writeValue(null)
     expect(component.value).toEqual([])
     expect(component.selectedDocuments).toEqual([])
@@ -100,9 +115,14 @@ describe('DocumentLinkComponent', () => {
   })
 
   it('should support unselect', () => {
-    const getSpy = jest.spyOn(documentService, 'getCachedMany')
+    const getSpy = jest.spyOn(documentService, 'getFew')
     getSpy.mockImplementation((ids) => {
-      return of(documents.filter((d) => ids.includes(d.id)))
+      const docs = documents.filter((d) => ids.includes(d.id))
+      return of({
+        count: docs.length,
+        all: docs.map((d) => d.id),
+        results: docs,
+      })
     })
     component.writeValue([12, 23])
     component.unselect({ id: 23 })
@@ -115,4 +135,26 @@ describe('DocumentLinkComponent', () => {
     expect(component.compareDocuments(documents[0], { id: 2 })).toBeFalsy()
     expect(component.trackByFn(documents[1])).toEqual(12)
   })
+
+  it('should not include the current document or already selected documents in results', () => {
+    let foundDocs
+    component.foundDocuments$.subscribe((found) => (foundDocs = found))
+    component.parentDocumentID = 23
+    component.selectedDocuments = [documents[2]]
+    const listSpy = jest.spyOn(documentService, 'listFiltered')
+    listSpy.mockImplementation(
+      (page, pageSize, sortField, sortReverse, filterRules, extraParams) => {
+        const docs = documents.filter((d) =>
+          d.title.includes(filterRules[0].value)
+        )
+        return of({
+          count: docs.length,
+          results: docs,
+          all: docs.map((d) => d.id),
+        })
+      }
+    )
+    component.documentsInput$.next('bar')
+    expect(foundDocs).toEqual([documents[1]])
+  })
 })
index dd711807426393b1ebd6246144d75ee27a0677c9..77a0fb99a3a47ec27f734f24789ab0cc43935b0d 100644 (file)
@@ -43,6 +43,9 @@ export class DocumentLinkComponent
   @Input()
   notFoundText: string = $localize`No documents found`
 
+  @Input()
+  parentDocumentID: number
+
   constructor(private documentsService: DocumentService) {
     super()
   }
@@ -58,11 +61,11 @@ export class DocumentLinkComponent
     } else {
       this.loading = true
       this.documentsService
-        .getCachedMany(documentIDs)
+        .getFew(documentIDs, { fields: 'id,title' })
         .pipe(takeUntil(this.unsubscribeNotifier))
-        .subscribe((documents) => {
+        .subscribe((documentResults) => {
           this.loading = false
-          this.selectedDocuments = documents
+          this.selectedDocuments = documentResults.results
           super.writeValue(documentIDs)
         })
     }
@@ -86,7 +89,13 @@ export class DocumentLinkComponent
               { truncate_content: true }
             )
             .pipe(
-              map((results) => results.results),
+              map((results) =>
+                results.results.filter(
+                  (d) =>
+                    d.id !== this.parentDocumentID &&
+                    !this.selectedDocuments.find((sd) => sd.id === d.id)
+                )
+              ),
               catchError(() => of([])), // empty on error
               tap(() => (this.loading = false))
             )
index 192680e57353ec84a38cd88afb3c6a55b340f88f..3211e60ed052f9fcd7bf0755b2cfd21088e7fa04 100644 (file)
                                     <pngx-input-number *ngSwitchCase="PaperlessCustomFieldDataType.Monetary" formControlName="value" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [showAdd]="false" [step]=".01" [error]="getCustomFieldError(i)"></pngx-input-number>
                                     <pngx-input-check *ngSwitchCase="PaperlessCustomFieldDataType.Boolean" formControlName="value" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-check>
                                     <pngx-input-url *ngSwitchCase="PaperlessCustomFieldDataType.Url" formControlName="value" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [error]="getCustomFieldError(i)"></pngx-input-url>
-                                    <pngx-input-document-link *ngSwitchCase="PaperlessCustomFieldDataType.DocumentLink" formControlName="value" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [error]="getCustomFieldError(i)"></pngx-input-document-link>
+                                    <pngx-input-document-link *ngSwitchCase="PaperlessCustomFieldDataType.DocumentLink" formControlName="value" [title]="getCustomFieldFromInstance(fieldInstance)?.name" [parentDocumentID]="documentId" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [error]="getCustomFieldError(i)"></pngx-input-document-link>
                                 </div>
                             </ng-container>
                         </div>
index 92ff923f48dfe0afe3536be7e12416bafdeb9ecb..7b5254bfdf19b9b6d4e39a1890d52e139a5e40d9 100644 (file)
@@ -96,6 +96,21 @@ export const commonAbstractPaperlessServiceTests = (endpoint, ServiceClass) => {
       expect(req.request.method).toEqual('PATCH')
       req.flush([])
     })
+
+    test('should call appropriate api endpoint for get a few objects', () => {
+      subscription = service.getFew([1, 2, 3]).subscribe()
+      const req = httpTestingController.expectOne(
+        `${environment.apiBaseUrl}${endpoint}/?id__in=1,2,3`
+      )
+      expect(req.request.method).toEqual('GET')
+      req.flush([])
+      subscription = service.getFew([4, 5, 6], { foo: 'bar' }).subscribe()
+      const req2 = httpTestingController.expectOne(
+        `${environment.apiBaseUrl}${endpoint}/?id__in=4,5,6&foo=bar`
+      )
+      expect(req2.request.method).toEqual('GET')
+      req2.flush([])
+    })
   })
 
   beforeEach(() => {
index 14735b1ad365eb8b6d85ce63a14ee95219cb10a3..9305ed8b6e91eb211d42033959f42c3c1510e6e1 100644 (file)
@@ -91,6 +91,19 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
     )
   }
 
+  getFew(ids: number[], extraParams?): Observable<Results<T>> {
+    let httpParams = new HttpParams()
+    httpParams = httpParams.set('id__in', ids.join(','))
+    for (let extraParamKey in extraParams) {
+      if (extraParams[extraParamKey] != null) {
+        httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
+      }
+    }
+    return this.http.get<Results<T>>(this.getResourceUrl(), {
+      params: httpParams,
+    })
+  }
+
   clearCache() {
     this._listAll = null
   }