Enhancement: use patch instead of put for frontend document changes ()

This commit is contained in:
shamoon 2025-04-22 12:58:28 -07:00 committed by GitHub
parent 2abf38f98e
commit cbaceb95af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 31 deletions

@ -456,11 +456,11 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
component.title = 'Foo Bar' component.title = 'Foo Bar'
const closeSpy = jest.spyOn(component, 'close') const closeSpy = jest.spyOn(component, 'close')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
const toastSpy = jest.spyOn(toastService, 'showInfo') const toastSpy = jest.spyOn(toastService, 'showInfo')
updateSpy.mockImplementation((o) => of(doc)) patchSpy.mockImplementation((o) => of(doc))
component.save(true) component.save(true)
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(closeSpy).toHaveBeenCalled() expect(closeSpy).toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith( expect(toastSpy).toHaveBeenCalledWith(
'Document "Doc 3" saved successfully.' 'Document "Doc 3" saved successfully.'
@ -471,11 +471,11 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
component.title = 'Foo Bar' component.title = 'Foo Bar'
const closeSpy = jest.spyOn(component, 'close') const closeSpy = jest.spyOn(component, 'close')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
const toastSpy = jest.spyOn(toastService, 'showInfo') const toastSpy = jest.spyOn(toastService, 'showInfo')
updateSpy.mockImplementation((o) => of(doc)) patchSpy.mockImplementation((o) => of(doc))
component.save() component.save()
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(closeSpy).not.toHaveBeenCalled() expect(closeSpy).not.toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith( expect(toastSpy).toHaveBeenCalledWith(
'Document "Doc 3" saved successfully.' 'Document "Doc 3" saved successfully.'
@ -487,12 +487,12 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
component.title = 'Foo Bar' component.title = 'Foo Bar'
const closeSpy = jest.spyOn(component, 'close') const closeSpy = jest.spyOn(component, 'close')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
const toastSpy = jest.spyOn(toastService, 'showError') const toastSpy = jest.spyOn(toastService, 'showError')
const error = new Error('failed to save') const error = new Error('failed to save')
updateSpy.mockImplementation(() => throwError(() => error)) patchSpy.mockImplementation(() => throwError(() => error))
component.save() component.save()
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(closeSpy).not.toHaveBeenCalled() expect(closeSpy).not.toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith( expect(toastSpy).toHaveBeenCalledWith(
'Error saving document "Doc 3"', 'Error saving document "Doc 3"',
@ -505,13 +505,13 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
component.title = 'Foo Bar' component.title = 'Foo Bar'
const closeSpy = jest.spyOn(component, 'close') const closeSpy = jest.spyOn(component, 'close')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
const toastSpy = jest.spyOn(toastService, 'showInfo') const toastSpy = jest.spyOn(toastService, 'showInfo')
updateSpy.mockImplementation(() => patchSpy.mockImplementation(() =>
throwError(() => new Error('failed to save')) throwError(() => new Error('failed to save'))
) )
component.save(true) component.save(true)
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(closeSpy).toHaveBeenCalled() expect(closeSpy).toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith( expect(toastSpy).toHaveBeenCalledWith(
'Document "Doc 3" saved successfully.' 'Document "Doc 3" saved successfully.'
@ -522,8 +522,8 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
const nextDocId = 100 const nextDocId = 100
component.title = 'Foo Bar' component.title = 'Foo Bar'
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
updateSpy.mockReturnValue(of(doc)) patchSpy.mockReturnValue(of(doc))
const nextSpy = jest.spyOn(documentListViewService, 'getNext') const nextSpy = jest.spyOn(documentListViewService, 'getNext')
nextSpy.mockReturnValue(of(nextDocId)) nextSpy.mockReturnValue(of(nextDocId))
const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument') const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument')
@ -531,7 +531,7 @@ describe('DocumentDetailComponent', () => {
const navigateSpy = jest.spyOn(router, 'navigate') const navigateSpy = jest.spyOn(router, 'navigate')
component.saveEditNext() component.saveEditNext()
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(navigateSpy).toHaveBeenCalledWith(['documents', nextDocId]) expect(navigateSpy).toHaveBeenCalledWith(['documents', nextDocId])
expect expect
}) })
@ -541,12 +541,12 @@ describe('DocumentDetailComponent', () => {
initNormally() initNormally()
component.title = 'Foo Bar' component.title = 'Foo Bar'
const closeSpy = jest.spyOn(component, 'close') const closeSpy = jest.spyOn(component, 'close')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
const toastSpy = jest.spyOn(toastService, 'showError') const toastSpy = jest.spyOn(toastService, 'showError')
const error = new Error('failed to save') const error = new Error('failed to save')
updateSpy.mockImplementation(() => throwError(() => error)) patchSpy.mockImplementation(() => throwError(() => error))
component.saveEditNext() component.saveEditNext()
expect(updateSpy).toHaveBeenCalled() expect(patchSpy).toHaveBeenCalled()
expect(closeSpy).not.toHaveBeenCalled() expect(closeSpy).not.toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith('Error saving document', error) expect(toastSpy).toHaveBeenCalledWith('Error saving document', error)
}) })
@ -965,10 +965,10 @@ describe('DocumentDetailComponent', () => {
expect(fixture.debugElement.nativeElement.textContent).toContain( expect(fixture.debugElement.nativeElement.textContent).toContain(
customFields[1].name customFields[1].name
) )
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
component.save(true) component.save(true)
expect(updateSpy.mock.lastCall[0].custom_fields).toHaveLength(2) expect(patchSpy.mock.lastCall[0].custom_fields).toHaveLength(2)
expect(updateSpy.mock.lastCall[0].custom_fields[1]).toEqual({ expect(patchSpy.mock.lastCall[0].custom_fields[1]).toEqual({
field: customFields[1].id, field: customFields[1].id,
value: null, value: null,
}) })
@ -985,9 +985,9 @@ describe('DocumentDetailComponent', () => {
expect( expect(
fixture.debugElement.query(By.css('form')).nativeElement.textContent fixture.debugElement.query(By.css('form')).nativeElement.textContent
).not.toContain('Field 1') ).not.toContain('Field 1')
const updateSpy = jest.spyOn(documentService, 'update') const patchSpy = jest.spyOn(documentService, 'patch')
component.save(true) component.save(true)
expect(updateSpy.mock.lastCall[0].custom_fields).toHaveLength( expect(patchSpy.mock.lastCall[0].custom_fields).toHaveLength(
initialLength - 1 initialLength - 1
) )
}) })

@ -784,6 +784,7 @@ export class DocumentDetailComponent
this.title = doc.title this.title = doc.title
this.updateFormForCustomFields() this.updateFormForCustomFields()
this.documentForm.patchValue(doc) this.documentForm.patchValue(doc)
this.documentForm.markAsPristine()
this.openDocumentService.setDirty(doc, false) this.openDocumentService.setDirty(doc, false)
}, },
error: () => { error: () => {
@ -794,11 +795,30 @@ export class DocumentDetailComponent
}) })
} }
private getChangedFields(): any {
const changes = {
id: this.document.id,
}
Object.keys(this.documentForm.controls).forEach((key) => {
if (this.documentForm.get(key).dirty) {
if (key === 'permissions_form') {
changes['owner'] =
this.documentForm.get('permissions_form').value['owner']
changes['set_permissions'] =
this.documentForm.get('permissions_form').value['set_permissions']
} else {
changes[key] = this.documentForm.get(key).value
}
}
})
return changes
}
save(close: boolean = false) { save(close: boolean = false) {
this.networkActive = true this.networkActive = true
;(document.activeElement as HTMLElement)?.dispatchEvent(new Event('change')) ;(document.activeElement as HTMLElement)?.dispatchEvent(new Event('change'))
this.documentsService this.documentsService
.update(this.document) .patch(this.getChangedFields())
.pipe(first()) .pipe(first())
.subscribe({ .subscribe({
next: (docValues) => { next: (docValues) => {
@ -852,7 +872,7 @@ export class DocumentDetailComponent
this.networkActive = true this.networkActive = true
this.store.next(this.documentForm.value) this.store.next(this.documentForm.value)
this.documentsService this.documentsService
.update(this.document) .patch(this.getChangedFields())
.pipe( .pipe(
switchMap((updateResult) => { switchMap((updateResult) => {
return this.documentListViewService return this.documentListViewService
@ -1303,6 +1323,8 @@ export class DocumentDetailComponent
created: new Date(), created: new Date(),
}) })
this.updateFormForCustomFields(true) this.updateFormForCustomFields(true)
this.documentForm.get('custom_fields').markAsDirty()
this.documentForm.updateValueAndValidity()
} }
public removeField(fieldInstance: CustomFieldInstance) { public removeField(fieldInstance: CustomFieldInstance) {
@ -1311,6 +1333,7 @@ export class DocumentDetailComponent
1 1
) )
this.updateFormForCustomFields(true) this.updateFormForCustomFields(true)
this.documentForm.get('custom_fields').markAsDirty()
this.documentForm.updateValueAndValidity() this.documentForm.updateValueAndValidity()
} }

@ -268,15 +268,15 @@ describe(`DocumentService`, () => {
expect(req.request.method).toEqual('GET') expect(req.request.method).toEqual('GET')
}) })
it('should pass remove_inbox_tags setting to update', () => { it('should pass remove_inbox_tags setting to patch', () => {
subscription = service.update(documents[0]).subscribe() subscription = service.patch(documents[0]).subscribe()
let req = httpTestingController.expectOne( let req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/` `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
) )
expect(req.request.body.remove_inbox_tags).toEqual(false) expect(req.request.body.remove_inbox_tags).toEqual(false)
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true) settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true)
subscription = service.update(documents[0]).subscribe() subscription = service.patch(documents[0]).subscribe()
req = httpTestingController.expectOne( req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/` `${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
) )

@ -189,13 +189,13 @@ export class DocumentService extends AbstractPaperlessService<Document> {
return this.http.get<number>(this.getResourceUrl(null, 'next_asn')) return this.http.get<number>(this.getResourceUrl(null, 'next_asn'))
} }
update(o: Document): Observable<Document> { patch(o: Document): Observable<Document> {
// we want to only set created_date // we want to only set created_date
o.created = undefined delete o.created
o.remove_inbox_tags = !!this.settingsService.get( o.remove_inbox_tags = !!this.settingsService.get(
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
) )
return super.update(o) return super.patch(o)
} }
uploadDocument(formData) { uploadDocument(formData) {