diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index b3952634c..7616e1b43 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -21,7 +21,7 @@ import { PermissionAction, PermissionType, } from './services/permissions.service' -import { ConsumptionTemplatesComponent } from './components/manage/consumption-templates/consumption-templates.component' +import { WorkflowsComponent } from './components/manage/workflows/workflows.component' import { MailComponent } from './components/manage/mail/mail.component' import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component' import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component' @@ -202,13 +202,13 @@ export const routes: Routes = [ }, }, { - path: 'templates', - component: ConsumptionTemplatesComponent, + path: 'workflows', + component: WorkflowsComponent, canActivate: [PermissionsGuard], data: { requiredPermission: { action: PermissionAction.View, - type: PermissionType.ConsumptionTemplate, + type: PermissionType.Workflow, }, }, }, diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index 2ab37d55e..fd2268818 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -176,9 +176,9 @@ export class AppComponent implements OnInit, OnDestroy { }, }, { - anchorId: 'tour.consumption-templates', - content: $localize`Consumption templates give you finer control over the document ingestion process.`, - route: '/templates', + anchorId: 'tour.workflows', + content: $localize`Workflows give you finer control over the document ingestion process.`, + route: '/workflows', backdropConfig: { offset: 0, }, diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index c3b98549a..400064f17 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -95,8 +95,8 @@ import { UsernamePipe } from './pipes/username.pipe' import { LogoComponent } from './components/common/logo/logo.component' import { IsNumberPipe } from './pipes/is-number.pipe' import { ShareLinksDropdownComponent } from './components/common/share-links-dropdown/share-links-dropdown.component' -import { ConsumptionTemplatesComponent } from './components/manage/consumption-templates/consumption-templates.component' -import { ConsumptionTemplateEditDialogComponent } from './components/common/edit-dialog/consumption-template-edit-dialog/consumption-template-edit-dialog.component' +import { WorkflowsComponent } from './components/manage/workflows/workflows.component' +import { WorkflowEditDialogComponent } from './components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component' import { MailComponent } from './components/manage/mail/mail.component' import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component' import { DragDropModule } from '@angular/cdk/drag-drop' @@ -251,8 +251,8 @@ function initializeApp(settings: SettingsService) { LogoComponent, IsNumberPipe, ShareLinksDropdownComponent, - ConsumptionTemplatesComponent, - ConsumptionTemplateEditDialogComponent, + WorkflowsComponent, + WorkflowEditDialogComponent, MailComponent, UsersAndGroupsComponent, FileDropComponent, diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 2ab3fe0ae..9aa8f84aa 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -235,14 +235,14 @@ - @for (template of templates; track template) { + @for (workflow of workflows; track workflow.id) {
  • -
    -
    {{template.order}}
    -
    {{getSourceList(template)}}
    +
    +
    {{workflow.order}}
    +
    {{getTypesList(workflow)}}
    - -
  • } - @if (templates.length === 0) { -
  • No templates defined.
  • + @if (workflows.length === 0) { +
  • No workflows defined.
  • } diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.scss b/src-ui/src/app/components/manage/workflows/workflows.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.spec.ts b/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts similarity index 70% rename from src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.spec.ts rename to src-ui/src/app/components/manage/workflows/workflows.component.spec.ts index 2cb365576..e24e7f8b8 100644 --- a/src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.spec.ts +++ b/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts @@ -9,55 +9,71 @@ import { NgbModalModule, } from '@ng-bootstrap/ng-bootstrap' import { of, throwError } from 'rxjs' -import { - DocumentSource, - ConsumptionTemplate, -} from 'src/app/data/consumption-template' +import { Workflow } from 'src/app/data/workflow' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' -import { ConsumptionTemplateService } from 'src/app/services/rest/consumption-template.service' +import { WorkflowService } from 'src/app/services/rest/workflow.service' import { ToastService } from 'src/app/services/toast.service' import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' import { PageHeaderComponent } from '../../common/page-header/page-header.component' -import { ConsumptionTemplatesComponent } from './consumption-templates.component' -import { ConsumptionTemplateEditDialogComponent } from '../../common/edit-dialog/consumption-template-edit-dialog/consumption-template-edit-dialog.component' +import { WorkflowsComponent } from './workflows.component' +import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component' import { PermissionsService } from 'src/app/services/permissions.service' +import { + DocumentSource, + WorkflowTriggerType, +} from 'src/app/data/workflow-trigger' -const templates: ConsumptionTemplate[] = [ +const workflows: Workflow[] = [ { - id: 0, - name: 'Template 1', - order: 0, - sources: [ - DocumentSource.ConsumeFolder, - DocumentSource.ApiUpload, - DocumentSource.MailFetch, + name: 'Workflow 1', + id: 1, + order: 1, + triggers: [ + { + id: 1, + type: WorkflowTriggerType.Consumption, + sources: [DocumentSource.ConsumeFolder], + filter_filename: '*', + }, + ], + actions: [ + { + id: 1, + assign_title: 'foo', + }, ], - filter_filename: 'foo', - filter_path: 'bar', - assign_tags: [1, 2, 3], }, { - id: 1, - name: 'Template 2', - order: 1, - sources: [DocumentSource.MailFetch], - filter_filename: null, - filter_path: 'foo/bar', - assign_owner: 1, + name: 'Workflow 2', + id: 2, + order: 2, + triggers: [ + { + id: 2, + type: WorkflowTriggerType.DocumentAdded, + filter_filename: 'foo', + }, + ], + actions: [ + { + id: 2, + assign_title: 'bar', + }, + ], }, ] -describe('ConsumptionTemplatesComponent', () => { - let component: ConsumptionTemplatesComponent - let fixture: ComponentFixture - let consumptionTemplateService: ConsumptionTemplateService +describe('WorkflowsComponent', () => { + let component: WorkflowsComponent + let fixture: ComponentFixture + let workflowService: WorkflowService let modalService: NgbModal let toastService: ToastService beforeEach(() => { TestBed.configureTestingModule({ declarations: [ - ConsumptionTemplatesComponent, + WorkflowsComponent, IfPermissionsDirective, PageHeaderComponent, ConfirmDialogComponent, @@ -81,18 +97,18 @@ describe('ConsumptionTemplatesComponent', () => { ], }) - consumptionTemplateService = TestBed.inject(ConsumptionTemplateService) - jest.spyOn(consumptionTemplateService, 'listAll').mockReturnValue( + workflowService = TestBed.inject(WorkflowService) + jest.spyOn(workflowService, 'listAll').mockReturnValue( of({ - count: templates.length, - all: templates.map((o) => o.id), - results: templates, + count: workflows.length, + all: workflows.map((o) => o.id), + results: workflows, }) ) modalService = TestBed.inject(NgbModal) toastService = TestBed.inject(ToastService) - fixture = TestBed.createComponent(ConsumptionTemplatesComponent) + fixture = TestBed.createComponent(WorkflowsComponent) component = fixture.componentInstance fixture.detectChanges() }) @@ -108,8 +124,7 @@ describe('ConsumptionTemplatesComponent', () => { createButton.triggerEventHandler('click') expect(modal).not.toBeUndefined() - const editDialog = - modal.componentInstance as ConsumptionTemplateEditDialogComponent + const editDialog = modal.componentInstance as WorkflowEditDialogComponent // fail first editDialog.failed.emit({ error: 'error creating item' }) @@ -117,7 +132,7 @@ describe('ConsumptionTemplatesComponent', () => { expect(reloadSpy).not.toHaveBeenCalled() // succeed - editDialog.succeeded.emit(templates[0]) + editDialog.succeeded.emit(workflows[0]) expect(toastInfoSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalled() }) @@ -133,9 +148,8 @@ describe('ConsumptionTemplatesComponent', () => { editButton.triggerEventHandler('click') expect(modal).not.toBeUndefined() - const editDialog = - modal.componentInstance as ConsumptionTemplateEditDialogComponent - expect(editDialog.object).toEqual(templates[0]) + const editDialog = modal.componentInstance as WorkflowEditDialogComponent + expect(editDialog.object).toEqual(workflows[0]) // fail first editDialog.failed.emit({ error: 'error editing item' }) @@ -143,7 +157,7 @@ describe('ConsumptionTemplatesComponent', () => { expect(reloadSpy).not.toHaveBeenCalled() // succeed - editDialog.succeeded.emit(templates[0]) + editDialog.succeeded.emit(workflows[0]) expect(toastInfoSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalled() }) @@ -152,7 +166,7 @@ describe('ConsumptionTemplatesComponent', () => { let modal: NgbModalRef modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1])) const toastErrorSpy = jest.spyOn(toastService, 'showError') - const deleteSpy = jest.spyOn(consumptionTemplateService, 'delete') + const deleteSpy = jest.spyOn(workflowService, 'delete') const reloadSpy = jest.spyOn(component, 'reload') const deleteButton = fixture.debugElement.queryAll(By.css('button'))[3] diff --git a/src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.ts b/src-ui/src/app/components/manage/workflows/workflows.component.ts similarity index 52% rename from src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.ts rename to src-ui/src/app/components/manage/workflows/workflows.component.ts index 301699abd..0be2db3f3 100644 --- a/src-ui/src/app/components/manage/consumption-templates/consumption-templates.component.ts +++ b/src-ui/src/app/components/manage/workflows/workflows.component.ts @@ -1,33 +1,34 @@ import { Component, OnInit } from '@angular/core' -import { ConsumptionTemplateService } from 'src/app/services/rest/consumption-template.service' +import { WorkflowService } from 'src/app/services/rest/workflow.service' import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' import { Subject, takeUntil } from 'rxjs' -import { ConsumptionTemplate } from 'src/app/data/consumption-template' +import { Workflow } from 'src/app/data/workflow' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { ToastService } from 'src/app/services/toast.service' import { PermissionsService } from 'src/app/services/permissions.service' import { - ConsumptionTemplateEditDialogComponent, + WorkflowEditDialogComponent, DOCUMENT_SOURCE_OPTIONS, -} from '../../common/edit-dialog/consumption-template-edit-dialog/consumption-template-edit-dialog.component' + WORKFLOW_TYPE_OPTIONS, +} from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component' import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' @Component({ - selector: 'pngx-consumption-templates', - templateUrl: './consumption-templates.component.html', - styleUrls: ['./consumption-templates.component.scss'], + selector: 'pngx-workflows', + templateUrl: './workflows.component.html', + styleUrls: ['./workflows.component.scss'], }) -export class ConsumptionTemplatesComponent +export class WorkflowsComponent extends ComponentWithPermissions implements OnInit { - public templates: ConsumptionTemplate[] = [] + public workflows: Workflow[] = [] private unsubscribeNotifier: Subject = new Subject() constructor( - private consumptionTemplateService: ConsumptionTemplateService, + private workflowService: WorkflowService, public permissionsService: PermissionsService, private modalService: NgbModal, private toastService: ToastService @@ -40,68 +41,68 @@ export class ConsumptionTemplatesComponent } reload() { - this.consumptionTemplateService + this.workflowService .listAll() .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((r) => { - this.templates = r.results + this.workflows = r.results }) } - getSourceList(template: ConsumptionTemplate): string { - return template.sources - .map((id) => DOCUMENT_SOURCE_OPTIONS.find((s) => s.id === id).name) + getTypesList(template: Workflow): string { + return template.triggers + .map( + (trigger) => + WORKFLOW_TYPE_OPTIONS.find((t) => t.id === trigger.type).name + ) .join(', ') } - editTemplate(rule: ConsumptionTemplate) { - const modal = this.modalService.open( - ConsumptionTemplateEditDialogComponent, - { - backdrop: 'static', - size: 'xl', - } - ) - modal.componentInstance.dialogMode = rule + editWorkflow(workflow: Workflow) { + const modal = this.modalService.open(WorkflowEditDialogComponent, { + backdrop: 'static', + size: 'xl', + }) + modal.componentInstance.dialogMode = workflow ? EditDialogMode.EDIT : EditDialogMode.CREATE - modal.componentInstance.object = rule + modal.componentInstance.object = workflow modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) - .subscribe((newTemplate) => { + .subscribe((newWorkflow) => { this.toastService.showInfo( - $localize`Saved template "${newTemplate.name}".` + $localize`Saved workflow "${newWorkflow.name}".` ) - this.consumptionTemplateService.clearCache() + this.workflowService.clearCache() this.reload() }) modal.componentInstance.failed .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((e) => { - this.toastService.showError($localize`Error saving template.`, e) + this.toastService.showError($localize`Error saving workflow.`, e) }) } - deleteTemplate(rule: ConsumptionTemplate) { + deleteWorkflow(workflow: Workflow) { const modal = this.modalService.open(ConfirmDialogComponent, { backdrop: 'static', }) - modal.componentInstance.title = $localize`Confirm delete template` - modal.componentInstance.messageBold = $localize`This operation will permanently delete this template.` + modal.componentInstance.title = $localize`Confirm delete workflow` + modal.componentInstance.messageBold = $localize`This operation will permanently delete this workflow.` modal.componentInstance.message = $localize`This operation cannot be undone.` modal.componentInstance.btnClass = 'btn-danger' modal.componentInstance.btnCaption = $localize`Proceed` modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.buttonsEnabled = false - this.consumptionTemplateService.delete(rule).subscribe({ + this.workflowService.delete(workflow).subscribe({ next: () => { modal.close() - this.toastService.showInfo($localize`Deleted template`) - this.consumptionTemplateService.clearCache() + this.toastService.showInfo($localize`Deleted workflow`) + this.workflowService.clearCache() this.reload() }, error: (e) => { - this.toastService.showError($localize`Error deleting template.`, e) + this.toastService.showError($localize`Error deleting workflow.`, e) }, }) }) diff --git a/src-ui/src/app/data/consumption-template.ts b/src-ui/src/app/data/workflow-action.ts similarity index 64% rename from src-ui/src/app/data/consumption-template.ts rename to src-ui/src/app/data/workflow-action.ts index cc85712c8..a313e0e63 100644 --- a/src-ui/src/app/data/consumption-template.ts +++ b/src-ui/src/app/data/workflow-action.ts @@ -1,24 +1,6 @@ import { ObjectWithId } from './object-with-id' -export enum DocumentSource { - ConsumeFolder = 1, - ApiUpload = 2, - MailFetch = 3, -} - -export interface ConsumptionTemplate extends ObjectWithId { - name: string - - order: number - - sources: DocumentSource[] - - filter_filename: string - - filter_path?: string - - filter_mailrule?: number // MailRule.id - +export interface WorkflowAction extends ObjectWithId { assign_title?: string assign_tags?: number[] // Tag.id diff --git a/src-ui/src/app/data/workflow-trigger.ts b/src-ui/src/app/data/workflow-trigger.ts new file mode 100644 index 000000000..c8745c4ac --- /dev/null +++ b/src-ui/src/app/data/workflow-trigger.ts @@ -0,0 +1,25 @@ +import { ObjectWithId } from './object-with-id' + +export enum DocumentSource { + ConsumeFolder = 1, + ApiUpload = 2, + MailFetch = 3, +} + +export enum WorkflowTriggerType { + Consumption = 1, + DocumentAdded = 2, + DocumentUpdated = 3, +} + +export interface WorkflowTrigger extends ObjectWithId { + type: WorkflowTriggerType + + sources?: DocumentSource[] + + filter_filename?: string + + filter_path?: string + + filter_mailrule?: number // MailRule.id +} diff --git a/src-ui/src/app/data/workflow.ts b/src-ui/src/app/data/workflow.ts new file mode 100644 index 000000000..9351c2fec --- /dev/null +++ b/src-ui/src/app/data/workflow.ts @@ -0,0 +1,13 @@ +import { ObjectWithId } from './object-with-id' +import { WorkflowAction } from './workflow-action' +import { WorkflowTrigger } from './workflow-trigger' + +export interface Workflow extends ObjectWithId { + name: string + + order: number + + triggers: WorkflowTrigger[] + + actions: WorkflowAction[] +} diff --git a/src-ui/src/app/services/permissions.service.spec.ts b/src-ui/src/app/services/permissions.service.spec.ts index 968082ae9..66276fbbb 100644 --- a/src-ui/src/app/services/permissions.service.spec.ts +++ b/src-ui/src/app/services/permissions.service.spec.ts @@ -252,10 +252,18 @@ describe('PermissionsService', () => { 'view_sharelink', 'change_sharelink', 'delete_sharelink', - 'add_consumptiontemplate', - 'view_consumptiontemplate', - 'change_consumptiontemplate', - 'delete_consumptiontemplate', + 'add_workflow', + 'view_workflow', + 'change_workflow', + 'delete_workflow', + 'add_workflowtrigger', + 'view_workflowtrigger', + 'change_workflowtrigger', + 'delete_workflowtrigger', + 'add_workflowaction', + 'view_workflowaction', + 'change_workflowaction', + 'delete_workflowaction', 'add_customfield', 'view_customfield', 'change_customfield', diff --git a/src-ui/src/app/services/permissions.service.ts b/src-ui/src/app/services/permissions.service.ts index a4e30d57e..3a1b99377 100644 --- a/src-ui/src/app/services/permissions.service.ts +++ b/src-ui/src/app/services/permissions.service.ts @@ -25,8 +25,10 @@ export enum PermissionType { Group = '%s_group', Admin = '%s_logentry', ShareLink = '%s_sharelink', - ConsumptionTemplate = '%s_consumptiontemplate', CustomField = '%s_customfield', + Workflow = '%s_workflow', + WorkflowTrigger = '%s_workflowtrigger', + WorkflowAction = '%s_workflowaction', } @Injectable({ diff --git a/src-ui/src/app/services/rest/workflow-action.service.spec.ts b/src-ui/src/app/services/rest/workflow-action.service.spec.ts new file mode 100644 index 000000000..817ec1584 --- /dev/null +++ b/src-ui/src/app/services/rest/workflow-action.service.spec.ts @@ -0,0 +1,48 @@ +import { HttpTestingController } from '@angular/common/http/testing' +import { TestBed } from '@angular/core/testing' +import { Subscription } from 'rxjs' +import { environment } from 'src/environments/environment' +import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' +import { WorkflowActionService } from './workflow-action.service' +import { WorkflowAction } from 'src/app/data/workflow-action' + +let httpTestingController: HttpTestingController +let service: WorkflowActionService +const endpoint = 'workflow_actions' +const actions: WorkflowAction[] = [ + { + id: 1, + assign_correspondent: 2, + }, + { + id: 2, + assign_document_type: 1, + }, +] + +// run common tests +commonAbstractPaperlessServiceTests(endpoint, WorkflowActionService) + +describe(`Additional service tests for WorkflowActionService`, () => { + it('should reload', () => { + service.reload() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000` + ) + req.flush({ + results: actions, + }) + expect(service.allActions).toEqual(actions) + }) + + beforeEach(() => { + // Dont need to setup again + + httpTestingController = TestBed.inject(HttpTestingController) + service = TestBed.inject(WorkflowActionService) + }) + + afterEach(() => { + httpTestingController.verify() + }) +}) diff --git a/src-ui/src/app/services/rest/consumption-template.service.ts b/src-ui/src/app/services/rest/workflow-action.service.ts similarity index 56% rename from src-ui/src/app/services/rest/consumption-template.service.ts rename to src-ui/src/app/services/rest/workflow-action.service.ts index eb932ebf7..c462f60a8 100644 --- a/src-ui/src/app/services/rest/consumption-template.service.ts +++ b/src-ui/src/app/services/rest/workflow-action.service.ts @@ -1,42 +1,43 @@ import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' import { tap } from 'rxjs' -import { ConsumptionTemplate } from 'src/app/data/consumption-template' +import { Workflow } from 'src/app/data/workflow' import { AbstractPaperlessService } from './abstract-paperless-service' +import { WorkflowAction } from 'src/app/data/workflow-action' @Injectable({ providedIn: 'root', }) -export class ConsumptionTemplateService extends AbstractPaperlessService { +export class WorkflowActionService extends AbstractPaperlessService { loading: boolean constructor(http: HttpClient) { - super(http, 'consumption_templates') + super(http, 'workflow_actions') } public reload() { this.loading = true this.listAll().subscribe((r) => { - this.templates = r.results + this.actions = r.results this.loading = false }) } - private templates: ConsumptionTemplate[] = [] + private actions: WorkflowAction[] = [] - public get allTemplates(): ConsumptionTemplate[] { - return this.templates + public get allActions(): WorkflowAction[] { + return this.actions } - create(o: ConsumptionTemplate) { + create(o: WorkflowAction) { return super.create(o).pipe(tap(() => this.reload())) } - update(o: ConsumptionTemplate) { + update(o: WorkflowAction) { return super.update(o).pipe(tap(() => this.reload())) } - delete(o: ConsumptionTemplate) { + delete(o: WorkflowAction) { return super.delete(o).pipe(tap(() => this.reload())) } } diff --git a/src-ui/src/app/services/rest/consumption-template.service.spec.ts b/src-ui/src/app/services/rest/workflow-trigger.service.spec.ts similarity index 57% rename from src-ui/src/app/services/rest/consumption-template.service.spec.ts rename to src-ui/src/app/services/rest/workflow-trigger.service.spec.ts index 920d0575c..01d72e1cc 100644 --- a/src-ui/src/app/services/rest/consumption-template.service.spec.ts +++ b/src-ui/src/app/services/rest/workflow-trigger.service.spec.ts @@ -1,61 +1,54 @@ import { HttpTestingController } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing' -import { Subscription } from 'rxjs' import { environment } from 'src/environments/environment' import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' -import { ConsumptionTemplateService } from './consumption-template.service' +import { WorkflowTriggerService } from './workflow-trigger.service' import { DocumentSource, - ConsumptionTemplate, -} from 'src/app/data/consumption-template' + WorkflowTrigger, + WorkflowTriggerType, +} from 'src/app/data/workflow-trigger' let httpTestingController: HttpTestingController -let service: ConsumptionTemplateService -const endpoint = 'consumption_templates' -const templates: ConsumptionTemplate[] = [ +let service: WorkflowTriggerService +const endpoint = 'workflow_triggers' +const triggers: WorkflowTrigger[] = [ { - name: 'Template 1', id: 1, - order: 1, + type: WorkflowTriggerType.Consumption, filter_filename: '*test*', filter_path: null, sources: [DocumentSource.ApiUpload], - assign_correspondent: 2, }, { - name: 'Template 2', id: 2, - order: 2, + type: WorkflowTriggerType.DocumentAdded, filter_filename: null, filter_path: '/test/', sources: [DocumentSource.ConsumeFolder, DocumentSource.ApiUpload], - assign_document_type: 1, }, ] // run common tests -commonAbstractPaperlessServiceTests( - 'consumption_templates', - ConsumptionTemplateService -) +commonAbstractPaperlessServiceTests(endpoint, WorkflowTriggerService) -describe(`Additional service tests for ConsumptionTemplateService`, () => { +describe(`Additional service tests for WorkflowTriggerService`, () => { it('should reload', () => { service.reload() const req = httpTestingController.expectOne( `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000` ) req.flush({ - results: templates, + results: triggers, }) - expect(service.allTemplates).toEqual(templates) + expect(service.allWorkflows).toEqual(triggers) }) beforeEach(() => { // Dont need to setup again httpTestingController = TestBed.inject(HttpTestingController) - service = TestBed.inject(ConsumptionTemplateService) + service = TestBed.inject(WorkflowTriggerService) }) afterEach(() => { diff --git a/src-ui/src/app/services/rest/workflow-trigger.service.ts b/src-ui/src/app/services/rest/workflow-trigger.service.ts new file mode 100644 index 000000000..963ed071e --- /dev/null +++ b/src-ui/src/app/services/rest/workflow-trigger.service.ts @@ -0,0 +1,42 @@ +import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { tap } from 'rxjs' +import { AbstractPaperlessService } from './abstract-paperless-service' +import { WorkflowTrigger } from 'src/app/data/workflow-trigger' + +@Injectable({ + providedIn: 'root', +}) +export class WorkflowTriggerService extends AbstractPaperlessService { + loading: boolean + + constructor(http: HttpClient) { + super(http, 'workflow_triggers') + } + + public reload() { + this.loading = true + this.listAll().subscribe((r) => { + this.triggers = r.results + this.loading = false + }) + } + + private triggers: WorkflowTrigger[] = [] + + public get allWorkflows(): WorkflowTrigger[] { + return this.triggers + } + + create(o: WorkflowTrigger) { + return super.create(o).pipe(tap(() => this.reload())) + } + + update(o: WorkflowTrigger) { + return super.update(o).pipe(tap(() => this.reload())) + } + + delete(o: WorkflowTrigger) { + return super.delete(o).pipe(tap(() => this.reload())) + } +} diff --git a/src-ui/src/app/services/rest/workflow.service.spec.ts b/src-ui/src/app/services/rest/workflow.service.spec.ts new file mode 100644 index 000000000..121fb9e21 --- /dev/null +++ b/src-ui/src/app/services/rest/workflow.service.spec.ts @@ -0,0 +1,80 @@ +import { HttpTestingController } from '@angular/common/http/testing' +import { TestBed } from '@angular/core/testing' +import { environment } from 'src/environments/environment' +import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' +import { WorkflowService } from './workflow.service' +import { Workflow } from 'src/app/data/workflow' +import { + DocumentSource, + WorkflowTriggerType, +} from 'src/app/data/workflow-trigger' + +let httpTestingController: HttpTestingController +let service: WorkflowService +const endpoint = 'workflows' +const workflows: Workflow[] = [ + { + name: 'Workflow 1', + id: 1, + order: 1, + triggers: [ + { + id: 1, + type: WorkflowTriggerType.Consumption, + sources: [DocumentSource.ConsumeFolder], + filter_filename: '*', + }, + ], + actions: [ + { + id: 1, + assign_title: 'foo', + }, + ], + }, + { + name: 'Workflow 2', + id: 2, + order: 2, + triggers: [ + { + id: 2, + type: WorkflowTriggerType.DocumentAdded, + filter_filename: 'foo', + }, + ], + actions: [ + { + id: 2, + assign_title: 'bar', + }, + ], + }, +] + +// run common tests +commonAbstractPaperlessServiceTests(endpoint, WorkflowService) + +describe(`Additional service tests for WorkflowService`, () => { + it('should reload', () => { + service.reload() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000` + ) + req.flush({ + results: workflows, + }) + expect(service.allWorkflows).toEqual(workflows) + }) + + beforeEach(() => { + // Dont need to setup again + + httpTestingController = TestBed.inject(HttpTestingController) + service = TestBed.inject(WorkflowService) + }) + + afterEach(() => { + httpTestingController.verify() + }) +}) diff --git a/src-ui/src/app/services/rest/workflow.service.ts b/src-ui/src/app/services/rest/workflow.service.ts new file mode 100644 index 000000000..0b489bc67 --- /dev/null +++ b/src-ui/src/app/services/rest/workflow.service.ts @@ -0,0 +1,42 @@ +import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { tap } from 'rxjs' +import { Workflow } from 'src/app/data/workflow' +import { AbstractPaperlessService } from './abstract-paperless-service' + +@Injectable({ + providedIn: 'root', +}) +export class WorkflowService extends AbstractPaperlessService { + loading: boolean + + constructor(http: HttpClient) { + super(http, 'workflows') + } + + public reload() { + this.loading = true + this.listAll().subscribe((r) => { + this.workflows = r.results + this.loading = false + }) + } + + private workflows: Workflow[] = [] + + public get allWorkflows(): Workflow[] { + return this.workflows + } + + create(o: Workflow) { + return super.create(o).pipe(tap(() => this.reload())) + } + + update(o: Workflow) { + return super.update(o).pipe(tap(() => this.reload())) + } + + delete(o: Workflow) { + return super.delete(o).pipe(tap(() => this.reload())) + } +} diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index e128b27fa..c8e8e8d5c 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -647,8 +647,6 @@ code { } .accordion { - --bs-accordion-btn-padding-x: 0.75rem; - --bs-accordion-btn-padding-y: 0.375rem; --bs-accordion-btn-bg: var(--bs-light); --bs-accordion-btn-color: var(--bs-primary); --bs-accordion-color: var(--bs-body-color);