mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-10-10 02:16:12 -05:00
Refactor workflow trigger conditions to filters
This commit is contained in:
@@ -176,58 +176,58 @@
|
||||
@if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
|
||||
<div class="row mt-3">
|
||||
<div class="col">
|
||||
<div class="trigger-conditions mb-3">
|
||||
<div class="trigger-filters mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="form-label mb-0" i18n>Conditions</label>
|
||||
<label class="form-label mb-0" i18n>Advanced Filters</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-primary ms-auto"
|
||||
(click)="addCondition(formGroup)"
|
||||
[disabled]="!canAddCondition(formGroup)"
|
||||
(click)="addFilter(formGroup)"
|
||||
[disabled]="!canAddFilter(formGroup)"
|
||||
>
|
||||
<i-bs name="plus-circle"></i-bs> <span i18n>Add condition</span>
|
||||
<i-bs name="plus-circle"></i-bs> <span i18n>Add filter</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="mt-2 list-group conditions" formArrayName="conditions">
|
||||
@if (getConditionsFormArray(formGroup).length === 0) {
|
||||
<p class="text-muted small" i18n>No conditions added. Add one to define document filters.</p>
|
||||
<ul class="mt-2 list-group filters" formArrayName="filters">
|
||||
@if (getFiltersFormArray(formGroup).length === 0) {
|
||||
<p class="text-muted small" i18n>No advanced workflow filters defined.</p>
|
||||
}
|
||||
@for (condition of getConditionsFormArray(formGroup).controls; track condition; let conditionIndex = $index) {
|
||||
<li [formGroupName]="conditionIndex" class="list-group-item">
|
||||
@for (filter of getFiltersFormArray(formGroup).controls; track filter; let filterIndex = $index) {
|
||||
<li [formGroupName]="filterIndex" class="list-group-item">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="w-25">
|
||||
<pngx-input-select
|
||||
i18n-title
|
||||
[items]="getConditionTypeOptions(formGroup, conditionIndex)"
|
||||
[items]="getFilterTypeOptions(formGroup, filterIndex)"
|
||||
formControlName="type"
|
||||
[allowNull]="false"
|
||||
></pngx-input-select>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
@if (isTagsCondition(condition.get('type').value)) {
|
||||
@if (isTagsFilter(filter.get('type').value)) {
|
||||
<pngx-input-tags
|
||||
[allowCreate]="false"
|
||||
[title]="null"
|
||||
formControlName="values"
|
||||
></pngx-input-tags>
|
||||
} @else if (
|
||||
isCustomFieldQueryCondition(condition.get('type').value)
|
||||
isCustomFieldQueryFilter(filter.get('type').value)
|
||||
) {
|
||||
<pngx-custom-fields-query-dropdown
|
||||
[selectionModel]="getCustomFieldQueryModel(condition)"
|
||||
(selectionModelChange)="onCustomFieldQuerySelectionChange(condition, $event)"
|
||||
[selectionModel]="getCustomFieldQueryModel(filter)"
|
||||
(selectionModelChange)="onCustomFieldQuerySelectionChange(filter, $event)"
|
||||
[useDropdown]="false"
|
||||
></pngx-custom-fields-query-dropdown>
|
||||
@if (!isCustomFieldQueryValid(condition)) {
|
||||
@if (!isCustomFieldQueryValid(filter)) {
|
||||
<div class="text-danger small" i18n>
|
||||
Complete the custom field query configuration.
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<pngx-input-select
|
||||
[items]="getConditionSelectItems(condition.get('type').value)"
|
||||
[items]="getFilterSelectItems(filter.get('type').value)"
|
||||
[allowNull]="true"
|
||||
[multiple]="isSelectMultiple(condition.get('type').value)"
|
||||
[multiple]="isSelectMultiple(filter.get('type').value)"
|
||||
formControlName="values"
|
||||
></pngx-input-select>
|
||||
}
|
||||
@@ -235,7 +235,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link text-danger p-0"
|
||||
(click)="removeCondition(formGroup, conditionIndex)"
|
||||
(click)="removeFilter(formGroup, filterIndex)"
|
||||
>
|
||||
<i-bs name="trash"></i-bs><span class="ms-1" i18n>Delete</span>
|
||||
</button>
|
||||
|
@@ -8,6 +8,6 @@
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
:host ::ng-deep .conditions .paperless-input-select.mb-3 {
|
||||
:host ::ng-deep .filters .paperless-input-select.mb-3 {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ import { EditDialogMode } from '../edit-dialog.component'
|
||||
import {
|
||||
DOCUMENT_SOURCE_OPTIONS,
|
||||
SCHEDULE_DATE_FIELD_OPTIONS,
|
||||
TriggerConditionType,
|
||||
TriggerFilterType,
|
||||
WORKFLOW_ACTION_OPTIONS,
|
||||
WORKFLOW_TYPE_OPTIONS,
|
||||
WorkflowEditDialogComponent,
|
||||
@@ -395,62 +395,50 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(component.matchingPatternRequired(triggerGroup)).toBe(false)
|
||||
})
|
||||
|
||||
it('should map condition builder values into trigger filters on save', () => {
|
||||
it('should map filter builder values into trigger filters on save', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
component.addFilter(triggerGroup as FormGroup)
|
||||
component.addFilter(triggerGroup as FormGroup)
|
||||
component.addFilter(triggerGroup as FormGroup)
|
||||
|
||||
const conditions = component.getConditionsFormArray(
|
||||
triggerGroup as FormGroup
|
||||
)
|
||||
expect(conditions.length).toBe(3)
|
||||
const filters = component.getFiltersFormArray(triggerGroup as FormGroup)
|
||||
expect(filters.length).toBe(3)
|
||||
|
||||
conditions.at(0).get('values').setValue([1])
|
||||
conditions.at(1).get('values').setValue([2, 3])
|
||||
conditions.at(2).get('values').setValue([4])
|
||||
filters.at(0).get('values').setValue([1])
|
||||
filters.at(1).get('values').setValue([2, 3])
|
||||
filters.at(2).get('values').setValue([4])
|
||||
|
||||
const addConditionOfType = (type: TriggerConditionType) => {
|
||||
const newCondition = component.addCondition(triggerGroup as FormGroup)
|
||||
newCondition.get('type').setValue(type)
|
||||
return newCondition
|
||||
const addFilterOfType = (type: TriggerFilterType) => {
|
||||
const newFilter = component.addFilter(triggerGroup as FormGroup)
|
||||
newFilter.get('type').setValue(type)
|
||||
return newFilter
|
||||
}
|
||||
|
||||
const correspondentIs = addConditionOfType(
|
||||
TriggerConditionType.CorrespondentIs
|
||||
)
|
||||
const correspondentIs = addFilterOfType(TriggerFilterType.CorrespondentIs)
|
||||
correspondentIs.get('values').setValue(1)
|
||||
|
||||
const correspondentNot = addConditionOfType(
|
||||
TriggerConditionType.CorrespondentNot
|
||||
)
|
||||
const correspondentNot = addFilterOfType(TriggerFilterType.CorrespondentNot)
|
||||
correspondentNot.get('values').setValue([1])
|
||||
|
||||
const documentTypeIs = addConditionOfType(
|
||||
TriggerConditionType.DocumentTypeIs
|
||||
)
|
||||
const documentTypeIs = addFilterOfType(TriggerFilterType.DocumentTypeIs)
|
||||
documentTypeIs.get('values').setValue(1)
|
||||
|
||||
const documentTypeNot = addConditionOfType(
|
||||
TriggerConditionType.DocumentTypeNot
|
||||
)
|
||||
const documentTypeNot = addFilterOfType(TriggerFilterType.DocumentTypeNot)
|
||||
documentTypeNot.get('values').setValue([1])
|
||||
|
||||
const storagePathIs = addConditionOfType(TriggerConditionType.StoragePathIs)
|
||||
const storagePathIs = addFilterOfType(TriggerFilterType.StoragePathIs)
|
||||
storagePathIs.get('values').setValue(1)
|
||||
|
||||
const storagePathNot = addConditionOfType(
|
||||
TriggerConditionType.StoragePathNot
|
||||
)
|
||||
const storagePathNot = addFilterOfType(TriggerFilterType.StoragePathNot)
|
||||
storagePathNot.get('values').setValue([1])
|
||||
|
||||
const customFieldCondition = addConditionOfType(
|
||||
TriggerConditionType.CustomFieldQuery
|
||||
const customFieldFilter = addFilterOfType(
|
||||
TriggerFilterType.CustomFieldQuery
|
||||
)
|
||||
const customFieldQuery = JSON.stringify(['AND', [[1, 'exact', 'test']]])
|
||||
customFieldCondition.get('values').setValue(customFieldQuery)
|
||||
customFieldFilter.get('values').setValue(customFieldQuery)
|
||||
|
||||
const formValues = component['getFormValues']()
|
||||
|
||||
@@ -466,23 +454,21 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(formValues.triggers[0].filter_custom_field_query).toEqual(
|
||||
customFieldQuery
|
||||
)
|
||||
expect(formValues.triggers[0].conditions).toBeUndefined()
|
||||
expect(formValues.triggers[0].filters).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should ignore empty and null condition values when mapping filters', () => {
|
||||
it('should ignore empty and null filter values when mapping filters', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
const tagsCondition = component.addCondition(triggerGroup)
|
||||
tagsCondition.get('type').setValue(TriggerConditionType.TagsAny)
|
||||
tagsCondition.get('values').setValue([])
|
||||
const tagsFilter = component.addFilter(triggerGroup)
|
||||
tagsFilter.get('type').setValue(TriggerFilterType.TagsAny)
|
||||
tagsFilter.get('values').setValue([])
|
||||
|
||||
const correspondentCondition = component.addCondition(triggerGroup)
|
||||
correspondentCondition
|
||||
.get('type')
|
||||
.setValue(TriggerConditionType.CorrespondentIs)
|
||||
correspondentCondition.get('values').setValue(null)
|
||||
const correspondentFilter = component.addFilter(triggerGroup)
|
||||
correspondentFilter.get('type').setValue(TriggerFilterType.CorrespondentIs)
|
||||
correspondentFilter.get('values').setValue(null)
|
||||
|
||||
const formValues = component['getFormValues']()
|
||||
|
||||
@@ -495,15 +481,15 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
const addConditionOfType = (type: TriggerConditionType, value: any) => {
|
||||
const condition = component.addCondition(triggerGroup)
|
||||
condition.get('type').setValue(type)
|
||||
condition.get('values').setValue(value)
|
||||
const addFilterOfType = (type: TriggerFilterType, value: any) => {
|
||||
const filter = component.addFilter(triggerGroup)
|
||||
filter.get('type').setValue(type)
|
||||
filter.get('values').setValue(value)
|
||||
}
|
||||
|
||||
addConditionOfType(TriggerConditionType.CorrespondentIs, [5])
|
||||
addConditionOfType(TriggerConditionType.DocumentTypeIs, [6])
|
||||
addConditionOfType(TriggerConditionType.StoragePathIs, [7])
|
||||
addFilterOfType(TriggerFilterType.CorrespondentIs, [5])
|
||||
addFilterOfType(TriggerFilterType.DocumentTypeIs, [6])
|
||||
addFilterOfType(TriggerFilterType.StoragePathIs, [7])
|
||||
|
||||
const formValues = component['getFormValues']()
|
||||
|
||||
@@ -512,22 +498,22 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(formValues.triggers[0].filter_has_storage_path).toEqual(7)
|
||||
})
|
||||
|
||||
it('should convert multi-value condition values when aggregating filters', () => {
|
||||
it('should convert multi-value filter values when aggregating filters', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
const setCondition = (type: TriggerConditionType, value: number): void => {
|
||||
const condition = component.addCondition(triggerGroup) as FormGroup
|
||||
condition.get('type').setValue(type)
|
||||
condition.get('values').setValue(value)
|
||||
const setFilter = (type: TriggerFilterType, value: number): void => {
|
||||
const filter = component.addFilter(triggerGroup) as FormGroup
|
||||
filter.get('type').setValue(type)
|
||||
filter.get('values').setValue(value)
|
||||
}
|
||||
|
||||
setCondition(TriggerConditionType.TagsAll, 11)
|
||||
setCondition(TriggerConditionType.TagsNone, 12)
|
||||
setCondition(TriggerConditionType.CorrespondentNot, 13)
|
||||
setCondition(TriggerConditionType.DocumentTypeNot, 14)
|
||||
setCondition(TriggerConditionType.StoragePathNot, 15)
|
||||
setFilter(TriggerFilterType.TagsAll, 11)
|
||||
setFilter(TriggerFilterType.TagsNone, 12)
|
||||
setFilter(TriggerFilterType.CorrespondentNot, 13)
|
||||
setFilter(TriggerFilterType.DocumentTypeNot, 14)
|
||||
setFilter(TriggerFilterType.StoragePathNot, 15)
|
||||
|
||||
const formValues = component['getFormValues']()
|
||||
|
||||
@@ -538,55 +524,55 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(formValues.triggers[0].filter_has_not_storage_paths).toEqual([15])
|
||||
})
|
||||
|
||||
it('should reuse condition type options and update disabled state', () => {
|
||||
it('should reuse filter type options and update disabled state', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
component.addCondition(triggerGroup)
|
||||
component.addFilter(triggerGroup)
|
||||
|
||||
const optionsFirst = component.getConditionTypeOptions(triggerGroup, 0)
|
||||
const optionsSecond = component.getConditionTypeOptions(triggerGroup, 0)
|
||||
const optionsFirst = component.getFilterTypeOptions(triggerGroup, 0)
|
||||
const optionsSecond = component.getFilterTypeOptions(triggerGroup, 0)
|
||||
expect(optionsFirst).toBe(optionsSecond)
|
||||
|
||||
// to force disabled flag
|
||||
component.addCondition(triggerGroup)
|
||||
const conditionArray = component.getConditionsFormArray(triggerGroup)
|
||||
const firstCondition = conditionArray.at(0)
|
||||
firstCondition.get('type').setValue(TriggerConditionType.CorrespondentIs)
|
||||
component.addFilter(triggerGroup)
|
||||
const filterArray = component.getFiltersFormArray(triggerGroup)
|
||||
const firstFilter = filterArray.at(0)
|
||||
firstFilter.get('type').setValue(TriggerFilterType.CorrespondentIs)
|
||||
|
||||
component.addCondition(triggerGroup)
|
||||
const updatedConditions = component.getConditionsFormArray(triggerGroup)
|
||||
const secondCondition = updatedConditions.at(1)
|
||||
const options = component.getConditionTypeOptions(triggerGroup, 1)
|
||||
component.addFilter(triggerGroup)
|
||||
const updatedFilters = component.getFiltersFormArray(triggerGroup)
|
||||
const secondFilter = updatedFilters.at(1)
|
||||
const options = component.getFilterTypeOptions(triggerGroup, 1)
|
||||
const correspondentIsOption = options.find(
|
||||
(option) => option.id === TriggerConditionType.CorrespondentIs
|
||||
(option) => option.id === TriggerFilterType.CorrespondentIs
|
||||
)
|
||||
expect(correspondentIsOption.disabled).toBe(true)
|
||||
|
||||
firstCondition.get('type').setValue(TriggerConditionType.DocumentTypeNot)
|
||||
secondCondition.get('type').setValue(TriggerConditionType.TagsAll)
|
||||
const postChangeOptions = component.getConditionTypeOptions(triggerGroup, 1)
|
||||
firstFilter.get('type').setValue(TriggerFilterType.DocumentTypeNot)
|
||||
secondFilter.get('type').setValue(TriggerFilterType.TagsAll)
|
||||
const postChangeOptions = component.getFilterTypeOptions(triggerGroup, 1)
|
||||
const correspondentOptionAfter = postChangeOptions.find(
|
||||
(option) => option.id === TriggerConditionType.CorrespondentIs
|
||||
(option) => option.id === TriggerFilterType.CorrespondentIs
|
||||
)
|
||||
expect(correspondentOptionAfter.disabled).toBe(false)
|
||||
})
|
||||
|
||||
it('should keep multi-entry condition options enabled and allow duplicates', () => {
|
||||
it('should keep multi-entry filter options enabled and allow duplicates', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
component.conditionDefinitions = [
|
||||
component.filterDefinitions = [
|
||||
{
|
||||
id: TriggerConditionType.TagsAny,
|
||||
id: TriggerFilterType.TagsAny,
|
||||
name: 'Any tags',
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: true,
|
||||
allowMultipleValues: true,
|
||||
} as any,
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentIs,
|
||||
id: TriggerFilterType.CorrespondentIs,
|
||||
name: 'Correspondent is',
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -595,36 +581,36 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
} as any,
|
||||
]
|
||||
|
||||
const firstCondition = component.addCondition(triggerGroup)
|
||||
firstCondition.get('type').setValue(TriggerConditionType.TagsAny)
|
||||
const firstFilter = component.addFilter(triggerGroup)
|
||||
firstFilter.get('type').setValue(TriggerFilterType.TagsAny)
|
||||
|
||||
const secondCondition = component.addCondition(triggerGroup)
|
||||
expect(secondCondition).not.toBeNull()
|
||||
const secondFilter = component.addFilter(triggerGroup)
|
||||
expect(secondFilter).not.toBeNull()
|
||||
|
||||
const options = component.getConditionTypeOptions(triggerGroup, 1)
|
||||
const options = component.getFilterTypeOptions(triggerGroup, 1)
|
||||
const multiEntryOption = options.find(
|
||||
(option) => option.id === TriggerConditionType.TagsAny
|
||||
(option) => option.id === TriggerFilterType.TagsAny
|
||||
)
|
||||
|
||||
expect(multiEntryOption.disabled).toBe(false)
|
||||
expect(component.canAddCondition(triggerGroup)).toBe(true)
|
||||
expect(component.canAddFilter(triggerGroup)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return null when no condition definitions remain available', () => {
|
||||
it('should return null when no filter definitions remain available', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
component.conditionDefinitions = [
|
||||
component.filterDefinitions = [
|
||||
{
|
||||
id: TriggerConditionType.TagsAny,
|
||||
id: TriggerFilterType.TagsAny,
|
||||
name: 'Any tags',
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
} as any,
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentIs,
|
||||
id: TriggerFilterType.CorrespondentIs,
|
||||
name: 'Correspondent is',
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -633,18 +619,18 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
} as any,
|
||||
]
|
||||
|
||||
const firstCondition = component.addCondition(triggerGroup)
|
||||
firstCondition.get('type').setValue(TriggerConditionType.TagsAny)
|
||||
const secondCondition = component.addCondition(triggerGroup)
|
||||
secondCondition.get('type').setValue(TriggerConditionType.CorrespondentIs)
|
||||
const firstFilter = component.addFilter(triggerGroup)
|
||||
firstFilter.get('type').setValue(TriggerFilterType.TagsAny)
|
||||
const secondFilter = component.addFilter(triggerGroup)
|
||||
secondFilter.get('type').setValue(TriggerFilterType.CorrespondentIs)
|
||||
|
||||
expect(component.canAddCondition(triggerGroup)).toBe(false)
|
||||
expect(component.addCondition(triggerGroup)).toBeNull()
|
||||
expect(component.canAddFilter(triggerGroup)).toBe(false)
|
||||
expect(component.addFilter(triggerGroup)).toBeNull()
|
||||
})
|
||||
|
||||
it('should skip condition definitions without handlers when building form array', () => {
|
||||
const originalDefinitions = component.conditionDefinitions
|
||||
component.conditionDefinitions = [
|
||||
it('should skip filter definitions without handlers when building form array', () => {
|
||||
const originalDefinitions = component.filterDefinitions
|
||||
component.filterDefinitions = [
|
||||
{
|
||||
id: 999,
|
||||
name: 'Unsupported',
|
||||
@@ -667,52 +653,52 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
filter_custom_field_query: null,
|
||||
} as any
|
||||
|
||||
const conditions = component['buildConditionFormArray'](trigger)
|
||||
expect(conditions.length).toBe(0)
|
||||
const filters = component['buildFiltersFormArray'](trigger)
|
||||
expect(filters.length).toBe(0)
|
||||
|
||||
component.conditionDefinitions = originalDefinitions
|
||||
component.filterDefinitions = originalDefinitions
|
||||
})
|
||||
|
||||
it('should return null when adding condition for unknown trigger form group', () => {
|
||||
expect(component.addCondition(new FormGroup({}) as any)).toBeNull()
|
||||
it('should return null when adding filter for unknown trigger form group', () => {
|
||||
expect(component.addFilter(new FormGroup({}) as any)).toBeNull()
|
||||
})
|
||||
|
||||
it('should ignore remove condition calls for unknown trigger form group', () => {
|
||||
it('should ignore remove filter calls for unknown trigger form group', () => {
|
||||
expect(() =>
|
||||
component.removeCondition(new FormGroup({}) as any, 0)
|
||||
component.removeFilter(new FormGroup({}) as any, 0)
|
||||
).not.toThrow()
|
||||
})
|
||||
|
||||
it('should teardown custom field query model when removing a custom field condition', () => {
|
||||
it('should teardown custom field query model when removing a custom field filter', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
component.addCondition(triggerGroup)
|
||||
const conditions = component.getConditionsFormArray(triggerGroup)
|
||||
const conditionGroup = conditions.at(0) as FormGroup
|
||||
conditionGroup.get('type').setValue(TriggerConditionType.CustomFieldQuery)
|
||||
component.addFilter(triggerGroup)
|
||||
const filters = component.getFiltersFormArray(triggerGroup)
|
||||
const filterGroup = filters.at(0) as FormGroup
|
||||
filterGroup.get('type').setValue(TriggerFilterType.CustomFieldQuery)
|
||||
|
||||
const model = component.getCustomFieldQueryModel(conditionGroup)
|
||||
const model = component.getCustomFieldQueryModel(filterGroup)
|
||||
expect(model).toBeDefined()
|
||||
expect(
|
||||
component['getStoredCustomFieldQueryModel'](conditionGroup as any)
|
||||
component['getStoredCustomFieldQueryModel'](filterGroup as any)
|
||||
).toBe(model)
|
||||
|
||||
component.removeCondition(triggerGroup, 0)
|
||||
component.removeFilter(triggerGroup, 0)
|
||||
expect(
|
||||
component['getStoredCustomFieldQueryModel'](conditionGroup as any)
|
||||
component['getStoredCustomFieldQueryModel'](filterGroup as any)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('should return readable condition names', () => {
|
||||
expect(component.getConditionName(TriggerConditionType.TagsAny)).toBe(
|
||||
it('should return readable filter names', () => {
|
||||
expect(component.getFilterName(TriggerFilterType.TagsAny)).toBe(
|
||||
'Has any of these tags'
|
||||
)
|
||||
expect(component.getConditionName(999 as any)).toBe('')
|
||||
expect(component.getFilterName(999 as any)).toBe('')
|
||||
})
|
||||
|
||||
it('should build condition form array from existing trigger filters', () => {
|
||||
it('should build filter form array from existing trigger filters', () => {
|
||||
const trigger = workflow.triggers[0]
|
||||
trigger.filter_has_tags = [1]
|
||||
trigger.filter_has_all_tags = [2, 3]
|
||||
@@ -731,64 +717,62 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
component.object = workflow
|
||||
component.ngOnInit()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
const conditions = component.getConditionsFormArray(triggerGroup)
|
||||
expect(conditions.length).toBe(10)
|
||||
const customFieldCondition = conditions.at(9) as FormGroup
|
||||
expect(customFieldCondition.get('type').value).toBe(
|
||||
TriggerConditionType.CustomFieldQuery
|
||||
const filters = component.getFiltersFormArray(triggerGroup)
|
||||
expect(filters.length).toBe(10)
|
||||
const customFieldFilter = filters.at(9) as FormGroup
|
||||
expect(customFieldFilter.get('type').value).toBe(
|
||||
TriggerFilterType.CustomFieldQuery
|
||||
)
|
||||
const model = component.getCustomFieldQueryModel(customFieldCondition)
|
||||
const model = component.getCustomFieldQueryModel(customFieldFilter)
|
||||
expect(model.isValid()).toBe(true)
|
||||
})
|
||||
|
||||
it('should expose select metadata helpers', () => {
|
||||
expect(
|
||||
component.isSelectMultiple(TriggerConditionType.CorrespondentNot)
|
||||
).toBe(true)
|
||||
expect(
|
||||
component.isSelectMultiple(TriggerConditionType.CorrespondentIs)
|
||||
).toBe(false)
|
||||
expect(component.isSelectMultiple(TriggerFilterType.CorrespondentNot)).toBe(
|
||||
true
|
||||
)
|
||||
expect(component.isSelectMultiple(TriggerFilterType.CorrespondentIs)).toBe(
|
||||
false
|
||||
)
|
||||
|
||||
component.correspondents = [{ id: 1, name: 'C1' } as any]
|
||||
component.documentTypes = [{ id: 2, name: 'DT' } as any]
|
||||
component.storagePaths = [{ id: 3, name: 'SP' } as any]
|
||||
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.CorrespondentIs)
|
||||
component.getFilterSelectItems(TriggerFilterType.CorrespondentIs)
|
||||
).toEqual(component.correspondents)
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.DocumentTypeIs)
|
||||
component.getFilterSelectItems(TriggerFilterType.DocumentTypeIs)
|
||||
).toEqual(component.documentTypes)
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.StoragePathIs)
|
||||
component.getFilterSelectItems(TriggerFilterType.StoragePathIs)
|
||||
).toEqual(component.storagePaths)
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.TagsAll)
|
||||
).toEqual([])
|
||||
expect(component.getFilterSelectItems(TriggerFilterType.TagsAll)).toEqual(
|
||||
[]
|
||||
)
|
||||
|
||||
expect(
|
||||
component.isCustomFieldQueryCondition(
|
||||
TriggerConditionType.CustomFieldQuery
|
||||
)
|
||||
component.isCustomFieldQueryFilter(TriggerFilterType.CustomFieldQuery)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('should return empty select items when definition is missing', () => {
|
||||
const originalDefinitions = component.conditionDefinitions
|
||||
component.conditionDefinitions = []
|
||||
const originalDefinitions = component.filterDefinitions
|
||||
component.filterDefinitions = []
|
||||
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.CorrespondentIs)
|
||||
component.getFilterSelectItems(TriggerFilterType.CorrespondentIs)
|
||||
).toEqual([])
|
||||
|
||||
component.conditionDefinitions = originalDefinitions
|
||||
component.filterDefinitions = originalDefinitions
|
||||
})
|
||||
|
||||
it('should return empty select items when definition has unknown source', () => {
|
||||
const originalDefinitions = component.conditionDefinitions
|
||||
component.conditionDefinitions = [
|
||||
const originalDefinitions = component.filterDefinitions
|
||||
component.filterDefinitions = [
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentIs,
|
||||
id: TriggerFilterType.CorrespondentIs,
|
||||
name: 'Correspondent is',
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -798,10 +782,10 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
]
|
||||
|
||||
expect(
|
||||
component.getConditionSelectItems(TriggerConditionType.CorrespondentIs)
|
||||
component.getFilterSelectItems(TriggerFilterType.CorrespondentIs)
|
||||
).toEqual([])
|
||||
|
||||
component.conditionDefinitions = originalDefinitions
|
||||
component.filterDefinitions = originalDefinitions
|
||||
})
|
||||
|
||||
it('should handle custom field query selection change and validation states', () => {
|
||||
@@ -837,19 +821,19 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
})
|
||||
|
||||
it('should recover from invalid custom field query json and update control on changes', () => {
|
||||
const conditionGroup = new FormGroup({
|
||||
const filterGroup = new FormGroup({
|
||||
values: new FormControl('not-json'),
|
||||
})
|
||||
|
||||
component['ensureCustomFieldQueryModel'](conditionGroup, 'not-json')
|
||||
component['ensureCustomFieldQueryModel'](filterGroup, 'not-json')
|
||||
|
||||
const model = component['getStoredCustomFieldQueryModel'](
|
||||
conditionGroup as any
|
||||
filterGroup as any
|
||||
)
|
||||
expect(model).toBeDefined()
|
||||
expect(model.queries.length).toBeGreaterThan(0)
|
||||
|
||||
const valuesControl = conditionGroup.get('values')
|
||||
const valuesControl = filterGroup.get('values')
|
||||
expect(valuesControl.value).toBeNull()
|
||||
|
||||
const expression = new CustomFieldQueryExpression([
|
||||
@@ -865,7 +849,7 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
|
||||
expect(valuesControl.value).toEqual(JSON.stringify(expression.serialize()))
|
||||
|
||||
component['clearCustomFieldQueryModel'](conditionGroup as any)
|
||||
component['clearCustomFieldQueryModel'](filterGroup as any)
|
||||
})
|
||||
|
||||
it('should handle custom field query model change edge cases', () => {
|
||||
@@ -898,71 +882,61 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(groupWithControl.get('values').value).toBeNull()
|
||||
})
|
||||
|
||||
it('should normalize condition values for single and multi selects', () => {
|
||||
it('should normalize filter values for single and multi selects', () => {
|
||||
expect(
|
||||
component['normalizeConditionValue'](TriggerConditionType.TagsAny)
|
||||
component['normalizeFilterValue'](TriggerFilterType.TagsAny)
|
||||
).toEqual([])
|
||||
expect(
|
||||
component['normalizeConditionValue'](TriggerConditionType.TagsAny, 5)
|
||||
component['normalizeFilterValue'](TriggerFilterType.TagsAny, 5)
|
||||
).toEqual([5])
|
||||
expect(
|
||||
component['normalizeConditionValue'](TriggerConditionType.TagsAny, [5, 6])
|
||||
component['normalizeFilterValue'](TriggerFilterType.TagsAny, [5, 6])
|
||||
).toEqual([5, 6])
|
||||
expect(
|
||||
component['normalizeConditionValue'](
|
||||
TriggerConditionType.CorrespondentIs,
|
||||
[7]
|
||||
)
|
||||
component['normalizeFilterValue'](TriggerFilterType.CorrespondentIs, [7])
|
||||
).toEqual(7)
|
||||
expect(
|
||||
component['normalizeConditionValue'](
|
||||
TriggerConditionType.CorrespondentIs,
|
||||
8
|
||||
)
|
||||
component['normalizeFilterValue'](TriggerFilterType.CorrespondentIs, 8)
|
||||
).toEqual(8)
|
||||
const customFieldJson = JSON.stringify(['AND', [[1, 'exact', 'test']]])
|
||||
expect(
|
||||
component['normalizeConditionValue'](
|
||||
TriggerConditionType.CustomFieldQuery,
|
||||
component['normalizeFilterValue'](
|
||||
TriggerFilterType.CustomFieldQuery,
|
||||
customFieldJson
|
||||
)
|
||||
).toEqual(customFieldJson)
|
||||
|
||||
const customFieldObject = ['AND', [[1, 'exact', 'other']]]
|
||||
expect(
|
||||
component['normalizeConditionValue'](
|
||||
TriggerConditionType.CustomFieldQuery,
|
||||
component['normalizeFilterValue'](
|
||||
TriggerFilterType.CustomFieldQuery,
|
||||
customFieldObject
|
||||
)
|
||||
).toEqual(JSON.stringify(customFieldObject))
|
||||
|
||||
expect(
|
||||
component['normalizeConditionValue'](
|
||||
TriggerConditionType.CustomFieldQuery,
|
||||
component['normalizeFilterValue'](
|
||||
TriggerFilterType.CustomFieldQuery,
|
||||
false
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('should add and remove condition form groups', () => {
|
||||
it('should add and remove filter form groups', () => {
|
||||
component['changeDetector'] = { detectChanges: jest.fn() } as any
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0) as FormGroup
|
||||
|
||||
component.addCondition(triggerGroup)
|
||||
component.addFilter(triggerGroup)
|
||||
|
||||
component.removeCondition(triggerGroup, 0)
|
||||
expect(component.getConditionsFormArray(triggerGroup).length).toBe(0)
|
||||
component.removeFilter(triggerGroup, 0)
|
||||
expect(component.getFiltersFormArray(triggerGroup).length).toBe(0)
|
||||
|
||||
component.addCondition(triggerGroup)
|
||||
const conditionArrayAfterAdd =
|
||||
component.getConditionsFormArray(triggerGroup)
|
||||
conditionArrayAfterAdd
|
||||
.at(0)
|
||||
.get('type')
|
||||
.setValue(TriggerConditionType.TagsAll)
|
||||
expect(component.getConditionsFormArray(triggerGroup).length).toBe(1)
|
||||
component.addFilter(triggerGroup)
|
||||
const filterArrayAfterAdd = component.getFiltersFormArray(triggerGroup)
|
||||
filterArrayAfterAdd.at(0).get('type').setValue(TriggerFilterType.TagsAll)
|
||||
expect(component.getFiltersFormArray(triggerGroup).length).toBe(1)
|
||||
})
|
||||
|
||||
it('should remove selected custom field from the form group', () => {
|
||||
|
@@ -141,7 +141,7 @@ export const WORKFLOW_ACTION_OPTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
export enum TriggerConditionType {
|
||||
export enum TriggerFilterType {
|
||||
TagsAny = 'tags_any',
|
||||
TagsAll = 'tags_all',
|
||||
TagsNone = 'tags_none',
|
||||
@@ -154,8 +154,8 @@ export enum TriggerConditionType {
|
||||
CustomFieldQuery = 'custom_field_query',
|
||||
}
|
||||
|
||||
interface TriggerConditionDefinition {
|
||||
id: TriggerConditionType
|
||||
interface TriggerFilterDefinition {
|
||||
id: TriggerFilterType
|
||||
name: string
|
||||
inputType: 'tags' | 'select' | 'customFieldQuery'
|
||||
allowMultipleEntries: boolean
|
||||
@@ -164,7 +164,7 @@ interface TriggerConditionDefinition {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type TriggerConditionOption = TriggerConditionDefinition & {
|
||||
type TriggerFilterOption = TriggerFilterDefinition & {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ type TriggerFilterAggregate = {
|
||||
filter_custom_field_query: string | null
|
||||
}
|
||||
|
||||
interface ConditionFilterHandler {
|
||||
interface FilterHandler {
|
||||
apply: (aggregate: TriggerFilterAggregate, values: any) => void
|
||||
extract: (trigger: WorkflowTrigger) => any
|
||||
hasValue: (value: any) => boolean
|
||||
@@ -192,35 +192,35 @@ const CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY = Symbol(
|
||||
'customFieldQuerySubscription'
|
||||
)
|
||||
|
||||
type CustomFieldConditionGroup = FormGroup & {
|
||||
type CustomFieldFilterGroup = FormGroup & {
|
||||
[CUSTOM_FIELD_QUERY_MODEL_KEY]?: CustomFieldQueriesModel
|
||||
[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?: Subscription
|
||||
}
|
||||
|
||||
const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
const TRIGGER_FILTER_DEFINITIONS: TriggerFilterDefinition[] = [
|
||||
{
|
||||
id: TriggerConditionType.TagsAny,
|
||||
id: TriggerFilterType.TagsAny,
|
||||
name: $localize`Has any of these tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.TagsAll,
|
||||
id: TriggerFilterType.TagsAll,
|
||||
name: $localize`Has all of these tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.TagsNone,
|
||||
id: TriggerFilterType.TagsNone,
|
||||
name: $localize`Does not have these tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentIs,
|
||||
id: TriggerFilterType.CorrespondentIs,
|
||||
name: $localize`Has correspondent`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -228,7 +228,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'correspondents',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentNot,
|
||||
id: TriggerFilterType.CorrespondentNot,
|
||||
name: $localize`Does not have correspondents`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -236,7 +236,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'correspondents',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.DocumentTypeIs,
|
||||
id: TriggerFilterType.DocumentTypeIs,
|
||||
name: $localize`Has document type`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -244,7 +244,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'documentTypes',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.DocumentTypeNot,
|
||||
id: TriggerFilterType.DocumentTypeNot,
|
||||
name: $localize`Does not have document types`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -252,7 +252,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'documentTypes',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.StoragePathIs,
|
||||
id: TriggerFilterType.StoragePathIs,
|
||||
name: $localize`Has storage path`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -260,7 +260,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'storagePaths',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.StoragePathNot,
|
||||
id: TriggerFilterType.StoragePathNot,
|
||||
name: $localize`Does not have storage paths`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
@@ -268,7 +268,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
selectItems: 'storagePaths',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.CustomFieldQuery,
|
||||
id: TriggerFilterType.CustomFieldQuery,
|
||||
name: $localize`Matches custom field query`,
|
||||
inputType: 'customFieldQuery',
|
||||
allowMultipleEntries: false,
|
||||
@@ -280,18 +280,15 @@ const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
|
||||
(a) => a.id !== MATCH_AUTO
|
||||
)
|
||||
|
||||
const CONDITION_FILTER_HANDLERS: Record<
|
||||
TriggerConditionType,
|
||||
ConditionFilterHandler
|
||||
> = {
|
||||
[TriggerConditionType.TagsAny]: {
|
||||
const FILTER_HANDLERS: Record<TriggerFilterType, FilterHandler> = {
|
||||
[TriggerFilterType.TagsAny]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_tags = Array.isArray(values) ? [...values] : [values]
|
||||
},
|
||||
extract: (trigger) => trigger.filter_has_tags,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.TagsAll]: {
|
||||
[TriggerFilterType.TagsAll]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_all_tags = Array.isArray(values)
|
||||
? [...values]
|
||||
@@ -300,7 +297,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_all_tags,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.TagsNone]: {
|
||||
[TriggerFilterType.TagsNone]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_not_tags = Array.isArray(values)
|
||||
? [...values]
|
||||
@@ -309,7 +306,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_not_tags,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.CorrespondentIs]: {
|
||||
[TriggerFilterType.CorrespondentIs]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_correspondent = Array.isArray(values)
|
||||
? (values[0] ?? null)
|
||||
@@ -318,7 +315,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_correspondent,
|
||||
hasValue: (value) => value !== null && value !== undefined,
|
||||
},
|
||||
[TriggerConditionType.CorrespondentNot]: {
|
||||
[TriggerFilterType.CorrespondentNot]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_not_correspondents = Array.isArray(values)
|
||||
? [...values]
|
||||
@@ -327,7 +324,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_not_correspondents,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.DocumentTypeIs]: {
|
||||
[TriggerFilterType.DocumentTypeIs]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_document_type = Array.isArray(values)
|
||||
? (values[0] ?? null)
|
||||
@@ -336,7 +333,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_document_type,
|
||||
hasValue: (value) => value !== null && value !== undefined,
|
||||
},
|
||||
[TriggerConditionType.DocumentTypeNot]: {
|
||||
[TriggerFilterType.DocumentTypeNot]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_not_document_types = Array.isArray(values)
|
||||
? [...values]
|
||||
@@ -345,7 +342,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_not_document_types,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.StoragePathIs]: {
|
||||
[TriggerFilterType.StoragePathIs]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_storage_path = Array.isArray(values)
|
||||
? (values[0] ?? null)
|
||||
@@ -354,7 +351,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_storage_path,
|
||||
hasValue: (value) => value !== null && value !== undefined,
|
||||
},
|
||||
[TriggerConditionType.StoragePathNot]: {
|
||||
[TriggerFilterType.StoragePathNot]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_has_not_storage_paths = Array.isArray(values)
|
||||
? [...values]
|
||||
@@ -363,7 +360,7 @@ const CONDITION_FILTER_HANDLERS: Record<
|
||||
extract: (trigger) => trigger.filter_has_not_storage_paths,
|
||||
hasValue: (value) => Array.isArray(value) && value.length > 0,
|
||||
},
|
||||
[TriggerConditionType.CustomFieldQuery]: {
|
||||
[TriggerFilterType.CustomFieldQuery]: {
|
||||
apply: (aggregate, values) => {
|
||||
aggregate.filter_custom_field_query = values as string
|
||||
},
|
||||
@@ -405,8 +402,8 @@ export class WorkflowEditDialogComponent
|
||||
{
|
||||
public WorkflowTriggerType = WorkflowTriggerType
|
||||
public WorkflowActionType = WorkflowActionType
|
||||
public TriggerConditionType = TriggerConditionType
|
||||
public conditionDefinitions = TRIGGER_CONDITION_DEFINITIONS
|
||||
public TriggerFilterType = TriggerFilterType
|
||||
public filterDefinitions = TRIGGER_FILTER_DEFINITIONS
|
||||
|
||||
private correspondentService: CorrespondentService
|
||||
private documentTypeService: DocumentTypeService
|
||||
@@ -426,9 +423,9 @@ export class WorkflowEditDialogComponent
|
||||
|
||||
private allowedActionTypes = []
|
||||
|
||||
private readonly triggerConditionOptionsMap = new WeakMap<
|
||||
private readonly triggerFilterOptionsMap = new WeakMap<
|
||||
FormArray,
|
||||
TriggerConditionOption[]
|
||||
TriggerFilterOption[]
|
||||
>()
|
||||
|
||||
constructor() {
|
||||
@@ -639,7 +636,7 @@ export class WorkflowEditDialogComponent
|
||||
formValues.triggers = formValues.triggers.map(
|
||||
(trigger: any, index: number) => {
|
||||
const triggerFormGroup = this.triggerFields.at(index) as FormGroup
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
const filters = this.getFiltersFormArray(triggerFormGroup)
|
||||
|
||||
const aggregate: TriggerFilterAggregate = {
|
||||
filter_has_tags: [],
|
||||
@@ -654,8 +651,8 @@ export class WorkflowEditDialogComponent
|
||||
filter_custom_field_query: null,
|
||||
}
|
||||
|
||||
for (const control of conditions.controls) {
|
||||
const type = control.get('type').value as TriggerConditionType
|
||||
for (const control of filters.controls) {
|
||||
const type = control.get('type').value as TriggerFilterType
|
||||
const values = control.get('values').value
|
||||
|
||||
if (values === null || values === undefined) {
|
||||
@@ -666,7 +663,7 @@ export class WorkflowEditDialogComponent
|
||||
continue
|
||||
}
|
||||
|
||||
const handler = CONDITION_FILTER_HANDLERS[type]
|
||||
const handler = FILTER_HANDLERS[type]
|
||||
handler?.apply(aggregate, values)
|
||||
}
|
||||
|
||||
@@ -688,7 +685,7 @@ export class WorkflowEditDialogComponent
|
||||
trigger.filter_custom_field_query =
|
||||
aggregate.filter_custom_field_query ?? null
|
||||
|
||||
delete trigger.conditions
|
||||
delete trigger.filters
|
||||
|
||||
return trigger
|
||||
}
|
||||
@@ -702,40 +699,38 @@ export class WorkflowEditDialogComponent
|
||||
return formGroup.get('matching_algorithm').value !== MATCH_NONE
|
||||
}
|
||||
|
||||
private createConditionFormGroup(
|
||||
type: TriggerConditionType,
|
||||
private createFilterFormGroup(
|
||||
type: TriggerFilterType,
|
||||
initialValue?: any
|
||||
): FormGroup {
|
||||
const group = new FormGroup({
|
||||
type: new FormControl(type),
|
||||
values: new FormControl(this.normalizeConditionValue(type, initialValue)),
|
||||
values: new FormControl(this.normalizeFilterValue(type, initialValue)),
|
||||
})
|
||||
|
||||
group
|
||||
.get('type')
|
||||
.valueChanges.subscribe((newType: TriggerConditionType) => {
|
||||
if (newType === TriggerConditionType.CustomFieldQuery) {
|
||||
this.ensureCustomFieldQueryModel(group)
|
||||
} else {
|
||||
this.clearCustomFieldQueryModel(group)
|
||||
group.get('values').setValue(this.getDefaultConditionValue(newType), {
|
||||
emitEvent: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
group.get('type').valueChanges.subscribe((newType: TriggerFilterType) => {
|
||||
if (newType === TriggerFilterType.CustomFieldQuery) {
|
||||
this.ensureCustomFieldQueryModel(group)
|
||||
} else {
|
||||
this.clearCustomFieldQueryModel(group)
|
||||
group.get('values').setValue(this.getDefaultFilterValue(newType), {
|
||||
emitEvent: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (type === TriggerConditionType.CustomFieldQuery) {
|
||||
if (type === TriggerFilterType.CustomFieldQuery) {
|
||||
this.ensureCustomFieldQueryModel(group, initialValue)
|
||||
}
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
private buildConditionFormArray(trigger: WorkflowTrigger): FormArray {
|
||||
const conditions = new FormArray([])
|
||||
private buildFiltersFormArray(trigger: WorkflowTrigger): FormArray {
|
||||
const filters = new FormArray([])
|
||||
|
||||
for (const definition of this.conditionDefinitions) {
|
||||
const handler = CONDITION_FILTER_HANDLERS[definition.id]
|
||||
for (const definition of this.filterDefinitions) {
|
||||
const handler = FILTER_HANDLERS[definition.id]
|
||||
if (!handler) {
|
||||
continue
|
||||
}
|
||||
@@ -745,24 +740,24 @@ export class WorkflowEditDialogComponent
|
||||
continue
|
||||
}
|
||||
|
||||
conditions.push(this.createConditionFormGroup(definition.id, value))
|
||||
filters.push(this.createFilterFormGroup(definition.id, value))
|
||||
}
|
||||
|
||||
return conditions
|
||||
return filters
|
||||
}
|
||||
|
||||
getConditionsFormArray(formGroup: FormGroup): FormArray {
|
||||
return formGroup.get('conditions') as FormArray
|
||||
getFiltersFormArray(formGroup: FormGroup): FormArray {
|
||||
return formGroup.get('filters') as FormArray
|
||||
}
|
||||
|
||||
getConditionTypeOptions(formGroup: FormGroup, conditionIndex: number) {
|
||||
const conditions = this.getConditionsFormArray(formGroup)
|
||||
const options = this.getConditionTypeOptionsForArray(conditions)
|
||||
const currentType = conditions.at(conditionIndex).get('type')
|
||||
.value as TriggerConditionType
|
||||
getFilterTypeOptions(formGroup: FormGroup, filterIndex: number) {
|
||||
const filters = this.getFiltersFormArray(formGroup)
|
||||
const options = this.getFilterTypeOptionsForArray(filters)
|
||||
const currentType = filters.at(filterIndex).get('type')
|
||||
.value as TriggerFilterType
|
||||
const usedTypes = new Set(
|
||||
conditions.controls.map(
|
||||
(control) => control.get('type').value as TriggerConditionType
|
||||
filters.controls.map(
|
||||
(control) => control.get('type').value as TriggerFilterType
|
||||
)
|
||||
)
|
||||
|
||||
@@ -778,15 +773,15 @@ export class WorkflowEditDialogComponent
|
||||
return options
|
||||
}
|
||||
|
||||
canAddCondition(formGroup: FormGroup): boolean {
|
||||
const conditions = this.getConditionsFormArray(formGroup)
|
||||
canAddFilter(formGroup: FormGroup): boolean {
|
||||
const filters = this.getFiltersFormArray(formGroup)
|
||||
const usedTypes = new Set(
|
||||
conditions.controls.map(
|
||||
(control) => control.get('type').value as TriggerConditionType
|
||||
filters.controls.map(
|
||||
(control) => control.get('type').value as TriggerFilterType
|
||||
)
|
||||
)
|
||||
|
||||
return this.conditionDefinitions.some((definition) => {
|
||||
return this.filterDefinitions.some((definition) => {
|
||||
if (definition.allowMultipleEntries) {
|
||||
return true
|
||||
}
|
||||
@@ -794,19 +789,19 @@ export class WorkflowEditDialogComponent
|
||||
})
|
||||
}
|
||||
|
||||
addCondition(triggerFormGroup: FormGroup): FormGroup | null {
|
||||
addFilter(triggerFormGroup: FormGroup): FormGroup | null {
|
||||
const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup)
|
||||
if (triggerIndex === -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
const filters = this.getFiltersFormArray(triggerFormGroup)
|
||||
|
||||
const availableDefinition = this.conditionDefinitions.find((definition) => {
|
||||
const availableDefinition = this.filterDefinitions.find((definition) => {
|
||||
if (definition.allowMultipleEntries) {
|
||||
return true
|
||||
}
|
||||
return !conditions.controls.some(
|
||||
return !filters.controls.some(
|
||||
(control) => control.get('type').value === definition.id
|
||||
)
|
||||
})
|
||||
@@ -815,72 +810,67 @@ export class WorkflowEditDialogComponent
|
||||
return null
|
||||
}
|
||||
|
||||
conditions.push(this.createConditionFormGroup(availableDefinition.id))
|
||||
filters.push(this.createFilterFormGroup(availableDefinition.id))
|
||||
triggerFormGroup.markAsDirty()
|
||||
triggerFormGroup.markAsTouched()
|
||||
|
||||
return conditions.at(-1) as FormGroup
|
||||
return filters.at(-1) as FormGroup
|
||||
}
|
||||
|
||||
removeCondition(triggerFormGroup: FormGroup, conditionIndex: number) {
|
||||
removeFilter(triggerFormGroup: FormGroup, filterIndex: number) {
|
||||
const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup)
|
||||
if (triggerIndex === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
const conditionGroup = conditions.at(conditionIndex) as FormGroup
|
||||
if (
|
||||
conditionGroup?.get('type').value ===
|
||||
TriggerConditionType.CustomFieldQuery
|
||||
) {
|
||||
this.clearCustomFieldQueryModel(conditionGroup)
|
||||
const filters = this.getFiltersFormArray(triggerFormGroup)
|
||||
const filterGroup = filters.at(filterIndex) as FormGroup
|
||||
if (filterGroup?.get('type').value === TriggerFilterType.CustomFieldQuery) {
|
||||
this.clearCustomFieldQueryModel(filterGroup)
|
||||
}
|
||||
conditions.removeAt(conditionIndex)
|
||||
filters.removeAt(filterIndex)
|
||||
triggerFormGroup.markAsDirty()
|
||||
triggerFormGroup.markAsTouched()
|
||||
}
|
||||
|
||||
getConditionDefinition(
|
||||
type: TriggerConditionType
|
||||
): TriggerConditionDefinition | undefined {
|
||||
return this.conditionDefinitions.find(
|
||||
(definition) => definition.id === type
|
||||
)
|
||||
getFilterDefinition(
|
||||
type: TriggerFilterType
|
||||
): TriggerFilterDefinition | undefined {
|
||||
return this.filterDefinitions.find((definition) => definition.id === type)
|
||||
}
|
||||
|
||||
getConditionName(type: TriggerConditionType): string {
|
||||
return this.getConditionDefinition(type)?.name ?? ''
|
||||
getFilterName(type: TriggerFilterType): string {
|
||||
return this.getFilterDefinition(type)?.name ?? ''
|
||||
}
|
||||
|
||||
isTagsCondition(type: TriggerConditionType): boolean {
|
||||
return this.getConditionDefinition(type)?.inputType === 'tags'
|
||||
isTagsFilter(type: TriggerFilterType): boolean {
|
||||
return this.getFilterDefinition(type)?.inputType === 'tags'
|
||||
}
|
||||
|
||||
isCustomFieldQueryCondition(type: TriggerConditionType): boolean {
|
||||
return this.getConditionDefinition(type)?.inputType === 'customFieldQuery'
|
||||
isCustomFieldQueryFilter(type: TriggerFilterType): boolean {
|
||||
return this.getFilterDefinition(type)?.inputType === 'customFieldQuery'
|
||||
}
|
||||
|
||||
isMultiValueCondition(type: TriggerConditionType): boolean {
|
||||
isMultiValueFilter(type: TriggerFilterType): boolean {
|
||||
switch (type) {
|
||||
case TriggerConditionType.TagsAny:
|
||||
case TriggerConditionType.TagsAll:
|
||||
case TriggerConditionType.TagsNone:
|
||||
case TriggerConditionType.CorrespondentNot:
|
||||
case TriggerConditionType.DocumentTypeNot:
|
||||
case TriggerConditionType.StoragePathNot:
|
||||
case TriggerFilterType.TagsAny:
|
||||
case TriggerFilterType.TagsAll:
|
||||
case TriggerFilterType.TagsNone:
|
||||
case TriggerFilterType.CorrespondentNot:
|
||||
case TriggerFilterType.DocumentTypeNot:
|
||||
case TriggerFilterType.StoragePathNot:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
isSelectMultiple(type: TriggerConditionType): boolean {
|
||||
return !this.isTagsCondition(type) && this.isMultiValueCondition(type)
|
||||
isSelectMultiple(type: TriggerFilterType): boolean {
|
||||
return !this.isTagsFilter(type) && this.isMultiValueFilter(type)
|
||||
}
|
||||
|
||||
getConditionSelectItems(type: TriggerConditionType) {
|
||||
const definition = this.getConditionDefinition(type)
|
||||
getFilterSelectItems(type: TriggerFilterType) {
|
||||
const definition = this.getFilterDefinition(type)
|
||||
if (!definition || definition.inputType !== 'select') {
|
||||
return []
|
||||
}
|
||||
@@ -917,36 +907,36 @@ export class WorkflowEditDialogComponent
|
||||
return model.isEmpty() || model.isValid()
|
||||
}
|
||||
|
||||
private getConditionTypeOptionsForArray(
|
||||
conditions: FormArray
|
||||
): TriggerConditionOption[] {
|
||||
let cached = this.triggerConditionOptionsMap.get(conditions)
|
||||
private getFilterTypeOptionsForArray(
|
||||
filters: FormArray
|
||||
): TriggerFilterOption[] {
|
||||
let cached = this.triggerFilterOptionsMap.get(filters)
|
||||
if (!cached) {
|
||||
cached = this.conditionDefinitions.map((definition) => ({
|
||||
cached = this.filterDefinitions.map((definition) => ({
|
||||
...definition,
|
||||
disabled: false,
|
||||
}))
|
||||
this.triggerConditionOptionsMap.set(conditions, cached)
|
||||
this.triggerFilterOptionsMap.set(filters, cached)
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
private ensureCustomFieldQueryModel(
|
||||
conditionGroup: FormGroup,
|
||||
filterGroup: FormGroup,
|
||||
initialValue?: any
|
||||
): CustomFieldQueriesModel {
|
||||
const existingModel = this.getStoredCustomFieldQueryModel(conditionGroup)
|
||||
const existingModel = this.getStoredCustomFieldQueryModel(filterGroup)
|
||||
if (existingModel) {
|
||||
return existingModel
|
||||
}
|
||||
|
||||
const model = new CustomFieldQueriesModel()
|
||||
this.setCustomFieldQueryModel(conditionGroup, model)
|
||||
this.setCustomFieldQueryModel(filterGroup, model)
|
||||
|
||||
const rawValue =
|
||||
typeof initialValue === 'string'
|
||||
? initialValue
|
||||
: (conditionGroup.get('values').value as string)
|
||||
: (filterGroup.get('values').value as string)
|
||||
|
||||
if (rawValue) {
|
||||
try {
|
||||
@@ -962,46 +952,45 @@ export class WorkflowEditDialogComponent
|
||||
const subscription = model.changed
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
this.onCustomFieldQueryModelChanged(conditionGroup, model)
|
||||
this.onCustomFieldQueryModelChanged(filterGroup, model)
|
||||
})
|
||||
conditionGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe()
|
||||
conditionGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY] = subscription
|
||||
filterGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe()
|
||||
filterGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY] = subscription
|
||||
|
||||
this.onCustomFieldQueryModelChanged(conditionGroup, model)
|
||||
this.onCustomFieldQueryModelChanged(filterGroup, model)
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
private clearCustomFieldQueryModel(conditionGroup: FormGroup) {
|
||||
const group = conditionGroup as CustomFieldConditionGroup
|
||||
private clearCustomFieldQueryModel(filterGroup: FormGroup) {
|
||||
const group = filterGroup as CustomFieldFilterGroup
|
||||
group[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe()
|
||||
delete group[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]
|
||||
delete group[CUSTOM_FIELD_QUERY_MODEL_KEY]
|
||||
}
|
||||
|
||||
private getStoredCustomFieldQueryModel(
|
||||
conditionGroup: FormGroup
|
||||
filterGroup: FormGroup
|
||||
): CustomFieldQueriesModel | null {
|
||||
return (
|
||||
(conditionGroup as CustomFieldConditionGroup)[
|
||||
CUSTOM_FIELD_QUERY_MODEL_KEY
|
||||
] ?? null
|
||||
(filterGroup as CustomFieldFilterGroup)[CUSTOM_FIELD_QUERY_MODEL_KEY] ??
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private setCustomFieldQueryModel(
|
||||
conditionGroup: FormGroup,
|
||||
filterGroup: FormGroup,
|
||||
model: CustomFieldQueriesModel
|
||||
) {
|
||||
const group = conditionGroup as CustomFieldConditionGroup
|
||||
const group = filterGroup as CustomFieldFilterGroup
|
||||
group[CUSTOM_FIELD_QUERY_MODEL_KEY] = model
|
||||
}
|
||||
|
||||
private onCustomFieldQueryModelChanged(
|
||||
conditionGroup: FormGroup,
|
||||
filterGroup: FormGroup,
|
||||
model: CustomFieldQueriesModel
|
||||
) {
|
||||
const control = conditionGroup.get('values')
|
||||
const control = filterGroup.get('values')
|
||||
if (!control) {
|
||||
return
|
||||
}
|
||||
@@ -1020,26 +1009,26 @@ export class WorkflowEditDialogComponent
|
||||
control.setValue(serialized, { emitEvent: false })
|
||||
}
|
||||
|
||||
private getDefaultConditionValue(type: TriggerConditionType) {
|
||||
if (type === TriggerConditionType.CustomFieldQuery) {
|
||||
private getDefaultFilterValue(type: TriggerFilterType) {
|
||||
if (type === TriggerFilterType.CustomFieldQuery) {
|
||||
return null
|
||||
}
|
||||
return this.isMultiValueCondition(type) ? [] : null
|
||||
return this.isMultiValueFilter(type) ? [] : null
|
||||
}
|
||||
|
||||
private normalizeConditionValue(type: TriggerConditionType, value?: any) {
|
||||
private normalizeFilterValue(type: TriggerFilterType, value?: any) {
|
||||
if (value === undefined || value === null) {
|
||||
return this.getDefaultConditionValue(type)
|
||||
return this.getDefaultFilterValue(type)
|
||||
}
|
||||
|
||||
if (type === TriggerConditionType.CustomFieldQuery) {
|
||||
if (type === TriggerFilterType.CustomFieldQuery) {
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
}
|
||||
return value ? JSON.stringify(value) : null
|
||||
}
|
||||
|
||||
if (this.isMultiValueCondition(type)) {
|
||||
if (this.isMultiValueFilter(type)) {
|
||||
return Array.isArray(value) ? [...value] : [value]
|
||||
}
|
||||
|
||||
@@ -1065,7 +1054,7 @@ export class WorkflowEditDialogComponent
|
||||
matching_algorithm: new FormControl(trigger.matching_algorithm),
|
||||
match: new FormControl(trigger.match),
|
||||
is_insensitive: new FormControl(trigger.is_insensitive),
|
||||
conditions: this.buildConditionFormArray(trigger),
|
||||
filters: this.buildFiltersFormArray(trigger),
|
||||
schedule_offset_days: new FormControl(trigger.schedule_offset_days),
|
||||
schedule_is_recurring: new FormControl(trigger.schedule_is_recurring),
|
||||
schedule_recurring_interval_days: new FormControl(
|
||||
|
Reference in New Issue
Block a user