diff --git a/packages/plugins/workflow/src/client/components/CollectionFieldset.tsx b/packages/plugins/workflow/src/client/components/CollectionFieldset.tsx index 385086f5a..dc0e59d3e 100644 --- a/packages/plugins/workflow/src/client/components/CollectionFieldset.tsx +++ b/packages/plugins/workflow/src/client/components/CollectionFieldset.tsx @@ -78,7 +78,7 @@ export default observer(({ value, disabled, onChange }: any) => { } `}> { onChange({ ...value, [field.name]: next }); diff --git a/packages/plugins/workflow/src/client/components/FieldsSelect.tsx b/packages/plugins/workflow/src/client/components/FieldsSelect.tsx new file mode 100644 index 000000000..29ccb5c38 --- /dev/null +++ b/packages/plugins/workflow/src/client/components/FieldsSelect.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Select } from 'antd'; +import { observer, useForm } from '@formily/react'; + +import { + useCollectionManager, + useCompile +} from '@nocobase/client'; + + + +export const FieldsSelect = observer((props: any) => { + const { filter = () => true, ...others } = props; + const compile = useCompile(); + const { getCollectionFields } = useCollectionManager(); + const { values } = useForm(); + const fields = getCollectionFields(values?.collection); + + return ( + - {fields - .filter(filter) - .map(field => ( - {compile(field.uiSchema?.title)} - ))} - - ); -}); +import { FieldsSelect } from '../components/FieldsSelect'; const COLLECTION_TRIGGER_MODE = { CREATED: 1, @@ -94,14 +72,6 @@ export default { }, } }, - { - target: 'changed', - fulfill: { - state: { - disabled: `{{!($self.value & ${COLLECTION_TRIGGER_MODE.UPDATED})}}`, - }, - } - }, ] }, changed: { @@ -123,10 +93,10 @@ export default { }, 'x-reactions': [ { - dependencies: ['collection'], + dependencies: ['collection', 'mode'], fulfill: { state: { - visible: '{{!!$deps[0]}}', + visible: `{{$deps[0] && $deps[1] & ${COLLECTION_TRIGGER_MODE.UPDATED}}}`, }, } }, @@ -146,27 +116,20 @@ export default { }, ] }, - // appends: { - // type: 'array', - // title: `{{t("Prefetch fields", { ns: "${NAMESPACE}" })}}`, - // description: `{{t("Triggered only if one of the selected fields changes. If unselected, it means that it will be triggered when any field changes. When record is added or deleted, any field is considered to have been changed.", { ns: "${NAMESPACE}" })}}`, - // 'x-decorator': 'FormItem', - // 'x-component': 'FieldsSelect', - // 'x-component-props': { - // mode: 'multiple', - // placeholder: '{{t("Select Field")}}' - // }, - // 'x-reactions': [ - // { - // dependencies: ['collection'], - // fulfill: { - // state: { - // visible: '{{!!$deps[0]}}', - // }, - // } - // }, - // ] - // }, + appends: { + ...appends, + 'x-reactions': [ + ...appends['x-reactions'], + { + dependencies: ['mode'], + fulfill: { + state: { + visible: `{{!($deps[0] & ${COLLECTION_TRIGGER_MODE.DELETED})}}`, + }, + } + }, + ] + }, }, scope: { useCollectionDataSource diff --git a/packages/plugins/workflow/src/client/triggers/index.tsx b/packages/plugins/workflow/src/client/triggers/index.tsx index 85b0b5d7b..c766026ae 100644 --- a/packages/plugins/workflow/src/client/triggers/index.tsx +++ b/packages/plugins/workflow/src/client/triggers/index.tsx @@ -164,7 +164,7 @@ export const TriggerConfig = () => { } const whiteSet = new Set(['workflow-node-meta', 'workflow-node-config-button', 'ant-input-disabled']); for (let el = ev.target; el && el !== ev.currentTarget; el = el.parentNode) { - if (Array.from(el.classList).some((name: string) => whiteSet.has(name))) { + if ((Array.from(el.classList) as string[]).some((name: string) => whiteSet.has(name))) { setEditingConfig(true); ev.stopPropagation(); return; @@ -234,7 +234,7 @@ export const TriggerConfig = () => { 'x-component': 'fieldset', 'x-component-props': { className: css` - .ant-select{ + .ant-select:not(.full-width){ width: auto; min-width: 6em; } @@ -242,28 +242,30 @@ export const TriggerConfig = () => { }, properties: fieldset }, - actions: executed - ? null - : { - type: 'void', - 'x-component': 'Action.Drawer.Footer', - properties: { - cancel: { - title: '{{t("Cancel")}}', - 'x-component': 'Action', - 'x-component-props': { - useAction: '{{ cm.useCancelAction }}', + actions: { + ...(executed + ? {} + : { + type: 'void', + 'x-component': 'Action.Drawer.Footer', + properties: { + cancel: { + title: '{{t("Cancel")}}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ cm.useCancelAction }}', + }, }, - }, - submit: { - title: '{{t("Submit")}}', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - useAction: useUpdateConfigAction + submit: { + title: '{{t("Submit")}}', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: useUpdateConfigAction + } } } - } + }) } } } diff --git a/packages/plugins/workflow/src/client/triggers/schedule/ScheduleConfig.tsx b/packages/plugins/workflow/src/client/triggers/schedule/ScheduleConfig.tsx index 7aedec349..da4802688 100644 --- a/packages/plugins/workflow/src/client/triggers/schedule/ScheduleConfig.tsx +++ b/packages/plugins/workflow/src/client/triggers/schedule/ScheduleConfig.tsx @@ -16,7 +16,6 @@ const ModeFieldsets = { [SCHEDULE_MODE.STATIC]: { startsOn: { type: 'datetime', - name: 'startsOn', title: `{{t("Starts on", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'DatePicker', @@ -27,7 +26,6 @@ const ModeFieldsets = { }, repeat: { type: 'string', - name: 'repeat', title: `{{t("Repeat mode", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'RepeatField', @@ -52,7 +50,6 @@ const ModeFieldsets = { }, endsOn: { type: 'datetime', - name: 'endsOn', title: `{{t("Ends on", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'DatePicker', @@ -62,7 +59,6 @@ const ModeFieldsets = { }, limit: { type: 'number', - name: 'limit', title: `{{t("Repeat limit", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'InputNumber', @@ -107,7 +103,6 @@ const ModeFieldsets = { }, repeat: { type: 'string', - name: 'repeat', title: `{{t("Repeat mode", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'RepeatField', @@ -138,7 +133,6 @@ const ModeFieldsets = { }, limit: { type: 'number', - name: 'limit', title: `{{t("Repeat limit", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'InputNumber', diff --git a/packages/plugins/workflow/src/client/triggers/schedule/index.tsx b/packages/plugins/workflow/src/client/triggers/schedule/index.tsx index 22ef545cc..a13554b6b 100644 --- a/packages/plugins/workflow/src/client/triggers/schedule/index.tsx +++ b/packages/plugins/workflow/src/client/triggers/schedule/index.tsx @@ -6,6 +6,8 @@ import { NAMESPACE, useWorkflowTranslation } from '../../locale'; import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers'; import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer'; import { useCollectionFieldOptions } from '../../variable'; +import { appends } from '../../schemas/collection'; +import { FieldsSelect } from '../../components/FieldsSelect'; @@ -18,13 +20,27 @@ export default { 'x-component': 'ScheduleConfig', 'x-component-props': { } - } + }, + appends: { + ...appends, + 'x-reactions': [ + { + dependencies: ['mode', 'collection'], + fulfill: { + state: { + visible: `{{$deps[0] === ${SCHEDULE_MODE.COLLECTION_FIELD} && $deps[1]}}`, + }, + } + }, + ] + }, }, scope: { useCollectionDataSource }, components: { - ScheduleConfig + ScheduleConfig, + FieldsSelect }, getOptions(config, types) { const { t } = useWorkflowTranslation(); diff --git a/packages/plugins/workflow/src/client/utils.ts b/packages/plugins/workflow/src/client/utils.ts index 9ca03bbb9..4658f9868 100644 --- a/packages/plugins/workflow/src/client/utils.ts +++ b/packages/plugins/workflow/src/client/utils.ts @@ -11,3 +11,26 @@ export function linkNodes(nodes): void { } } } + +export function isValidFilter(condition) { + const group = condition.$and || condition.$or; + if (!group) { + return false; + } + + return group.some(item => { + if (item.$and || item.$or) { + return isValidFilter(item); + } + const [name] = Object.keys(item); + if (!name || !item[name]) { + return false; + } + const [op] = Object.keys(item[name]); + if (!op || typeof item[name][op] === 'undefined') { + return false; + } + + return true; + }); +} diff --git a/packages/plugins/workflow/src/client/variable.tsx b/packages/plugins/workflow/src/client/variable.tsx index 8e6bc5302..0dc9a71a2 100644 --- a/packages/plugins/workflow/src/client/variable.tsx +++ b/packages/plugins/workflow/src/client/variable.tsx @@ -5,7 +5,12 @@ import { useFlowContext } from "./FlowContext"; import { triggers } from "./triggers"; import { NAMESPACE } from "./locale"; -export type VariableOption = { key: string, value: string; label: string; children?: VariableOption[] }; +export type VariableOption = { + key: string; + value: string; + label: string; + children?: VariableOption[] +}; const VariableTypes = [ { @@ -97,17 +102,26 @@ export function useCollectionFieldOptions(props) { const { fields, collection, types } = props; const compile = useCompile(); const { getCollectionFields } = useCollectionManager(); - return filterTypedFields((fields ?? getCollectionFields(collection)), types) - .filter(field => field.interface && (!field.target || field.type === 'belongsTo')) - .map(field => field.type === 'belongsTo' - ? { - label: `${compile(field.uiSchema?.title || field.name)} ID`, - key: field.foreignKey, - value: field.foreignKey, - } - : { + const result = filterTypedFields((fields ?? getCollectionFields(collection)), types) + .map(field => ({ label: compile(field.uiSchema?.title || field.name), key: field.name, value: field.name, - }); + children: ['linkTo', 'belongsTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.type) + ? getCollectionFields(field.target)?.filter(subField => subField.interface && (!subField.target || subField.type === 'belongsTo')) + .map(subField => subField.type === 'belongsTo' + ? { + label: `${compile(subField.uiSchema?.title || subField.name)} ID`, + key: subField.foreignKey, + value: subField.foreignKey, + } + : { + label: compile(subField.uiSchema?.title || subField.name), + key: subField.name, + value: subField.name, + }) + : null + })); + + return result; } diff --git a/packages/plugins/workflow/src/server/__tests__/collections/categories.ts b/packages/plugins/workflow/src/server/__tests__/collections/categories.ts new file mode 100644 index 000000000..e5fcce1a6 --- /dev/null +++ b/packages/plugins/workflow/src/server/__tests__/collections/categories.ts @@ -0,0 +1,11 @@ +import { CollectionOptions } from '@nocobase/database'; + +export default { + name: 'categories', + fields: [ + { + type: 'string', + name: 'title', + } + ], +} as CollectionOptions; diff --git a/packages/plugins/workflow/src/server/__tests__/collections/comments.ts b/packages/plugins/workflow/src/server/__tests__/collections/comments.ts index dfb104209..9a063b11c 100644 --- a/packages/plugins/workflow/src/server/__tests__/collections/comments.ts +++ b/packages/plugins/workflow/src/server/__tests__/collections/comments.ts @@ -11,6 +11,10 @@ export default { type: 'integer', name: 'status', defaultValue: 0 + }, + { + type: 'hasMany', + name: 'replies', } ], } as CollectionOptions; diff --git a/packages/plugins/workflow/src/server/__tests__/collections/posts.ts b/packages/plugins/workflow/src/server/__tests__/collections/posts.ts index 30afa1a7c..07f9015a9 100644 --- a/packages/plugins/workflow/src/server/__tests__/collections/posts.ts +++ b/packages/plugins/workflow/src/server/__tests__/collections/posts.ts @@ -17,6 +17,10 @@ export default { name: 'read', defaultValue: 0 }, + { + type: 'belongsTo', + name: 'category' + }, { type: 'hasMany', name: 'comments' diff --git a/packages/plugins/workflow/src/server/__tests__/collections/replies.ts b/packages/plugins/workflow/src/server/__tests__/collections/replies.ts new file mode 100644 index 000000000..cc634546f --- /dev/null +++ b/packages/plugins/workflow/src/server/__tests__/collections/replies.ts @@ -0,0 +1,9 @@ +export default { + name: 'replies', + fields: [ + { + type: 'string', + name: 'content', + } + ] +} diff --git a/packages/plugins/workflow/src/server/__tests__/instructions/create.test.ts b/packages/plugins/workflow/src/server/__tests__/instructions/create.test.ts index 726d825c7..428e2ef6c 100644 --- a/packages/plugins/workflow/src/server/__tests__/instructions/create.test.ts +++ b/packages/plugins/workflow/src/server/__tests__/instructions/create.test.ts @@ -8,6 +8,7 @@ describe('workflow > instructions > create', () => { let app: Application; let db: Database; let PostRepo; + let ReplyRepo; let WorkflowModel; let workflow; @@ -17,6 +18,7 @@ describe('workflow > instructions > create', () => { db = app.db; WorkflowModel = db.getCollection('workflows').model; PostRepo = db.getCollection('posts').repository; + ReplyRepo = db.getCollection('replies').repository; workflow = await WorkflowModel.create({ title: 'test workflow', @@ -53,5 +55,77 @@ describe('workflow > instructions > create', () => { const [job] = await execution.getJobs(); expect(job.result.postId).toBe(post.id); }); + + it('params.values with hasMany', async () => { + const replies = await ReplyRepo.create({ values: [{}, {}] }); + + const n1 = await workflow.createNode({ + type: 'create', + config: { + collection: 'comments', + params: { + values: { + replies: replies.map(item => item.id) + }, + appends: ['replies'] + } + } + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + const [job] = await execution.getJobs(); + expect(job.result.replies.length).toBe(2); + }); + + it('params.appends: belongsTo', async () => { + const n1 = await workflow.createNode({ + type: 'create', + config: { + collection: 'comments', + params: { + values: { + postId: '{{$context.data.id}}' + }, + appends: ['post'], + } + } + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + const [job] = await execution.getJobs(); + expect(job.result.post.id).toBe(post.id); + }); + + it('params.appends: belongsToMany', async () => { + const n1 = await workflow.createNode({ + type: 'create', + config: { + collection: 'tags', + params: { + values: { + posts: ['{{$context.data.id}}'] + }, + appends: ['posts'], + } + } + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + const [job] = await execution.getJobs(); + expect(job.result.posts.length).toBe(1); + expect(job.result.posts[0].id).toBe(post.id); + }); }); }); diff --git a/packages/plugins/workflow/src/server/__tests__/instructions/query.test.ts b/packages/plugins/workflow/src/server/__tests__/instructions/query.test.ts index 6f04d725b..c03ad364c 100644 --- a/packages/plugins/workflow/src/server/__tests__/instructions/query.test.ts +++ b/packages/plugins/workflow/src/server/__tests__/instructions/query.test.ts @@ -10,6 +10,7 @@ describe('workflow > instructions > query', () => { let PostCollection; let PostRepo; let TagModel; + let CommentRepo; let WorkflowModel; let workflow; @@ -20,6 +21,7 @@ describe('workflow > instructions > query', () => { WorkflowModel = db.getCollection('workflows').model; PostCollection = db.getCollection('posts'); PostRepo = PostCollection.repository; + CommentRepo = db.getCollection('comments').repository; TagModel = db.getCollection('tags').model; workflow = await WorkflowModel.create({ @@ -160,7 +162,7 @@ describe('workflow > instructions > query', () => { }); const tag = await TagModel.create({ name: 'tag1' }); - const post = await PostCollection.repository.create({ + const post = await PostRepo.create({ values: { title: 't1', tags: [tag.id] } }); @@ -171,7 +173,31 @@ describe('workflow > instructions > query', () => { expect(job.result.id).toBe(tag.id); }); - it('params.appends: with associations', async () => { + it('params.appends: hasMany', async () => { + const n1 = await workflow.createNode({ + type: 'query', + config: { + collection: 'posts', + params: { + appends: ['comments'] + } + } + }); + + const comment = await CommentRepo.create({}); + const post = await PostRepo.create({ + values: { title: 't1', comments: [comment.id] } + }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + const [job] = await execution.getJobs(); + expect(job.result.comments.length).toBe(1); + expect(job.result.comments[0].id).toBe(comment.id); + }); + + it('params.appends: belongsToMany', async () => { const n1 = await workflow.createNode({ type: 'query', config: { @@ -183,7 +209,7 @@ describe('workflow > instructions > query', () => { }); const tag = await TagModel.create({ name: 'tag1' }); - const post = await PostCollection.repository.create({ + const post = await PostRepo.create({ values: { title: 't1', tags: [tag.id] } }); diff --git a/packages/plugins/workflow/src/server/__tests__/triggers/collection.test.ts b/packages/plugins/workflow/src/server/__tests__/triggers/collection.test.ts index 124fa9be8..abeaf8691 100644 --- a/packages/plugins/workflow/src/server/__tests__/triggers/collection.test.ts +++ b/packages/plugins/workflow/src/server/__tests__/triggers/collection.test.ts @@ -8,7 +8,10 @@ import { EXECUTION_STATUS } from '../../constants'; describe('workflow > triggers > collection', () => { let app: Application; let db: Database; + let CategoryRepo; let PostRepo; + let CommentRepo; + let TagRepo; let WorkflowModel; beforeEach(async () => { @@ -16,7 +19,10 @@ describe('workflow > triggers > collection', () => { db = app.db; WorkflowModel = db.getCollection('workflows').model; + CategoryRepo = db.getCollection('categories').repository; PostRepo = db.getCollection('posts').repository; + CommentRepo = db.getCollection('comments').repository; + TagRepo = db.getCollection('tags').repository; }); afterEach(() => db.close()); @@ -111,4 +117,133 @@ describe('workflow > triggers > collection', () => { expect(executions.length).toBe(0); }); }); + + describe('config.appends', () => { + it('non-appended association could not be accessed', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + } + }); + + await workflow.createNode({ + type: 'echo' + }); + + const category = await CategoryRepo.create({ values: { title: 'c1' } }); + + const post = await PostRepo.create({ + values: { + title: 't1', + categoryId: category.id + } + }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const [job] = await execution.getJobs(); + expect(job.result.data.category).toBeUndefined(); + }); + + it('appends association could be accessed', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + appends: ['category'] + } + }); + + await workflow.createNode({ + type: 'echo' + }); + + const category = await CategoryRepo.create({ values: { title: 'c1' } }); + + const post = await PostRepo.create({ + values: { + title: 't1', + categoryId: category.id + } + }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const [job] = await execution.getJobs(); + expect(job.result.data.category.title).toBe('c1'); + }); + + it('appends hasMany', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + appends: ['comments'] + } + }); + + await workflow.createNode({ + type: 'echo' + }); + + const comments = await CommentRepo.create({ values: [{}] }); + + const post = await PostRepo.create({ + values: { + title: 't1', + comments: comments.map(item => item.id) + } + }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const [job] = await execution.getJobs(); + expect(job.result.data.comments.length).toBe(1); + }); + + it('appends belongsToMany', async () => { + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'collection', + config: { + mode: 1, + collection: 'posts', + appends: ['tags'] + } + }); + + await workflow.createNode({ + type: 'echo' + }); + + const tags = await TagRepo.create({ values: [{}] }); + + const post = await PostRepo.create({ + values: { + title: 't1', + tags: tags.map(item => item.id) + } + }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED); + const [job] = await execution.getJobs(); + expect(job.result.data.tags.length).toBe(1); + }); + }); }); diff --git a/packages/plugins/workflow/src/server/__tests__/triggers/schedule.test.ts b/packages/plugins/workflow/src/server/__tests__/triggers/schedule.test.ts index c58a10ca9..324db0cfd 100644 --- a/packages/plugins/workflow/src/server/__tests__/triggers/schedule.test.ts +++ b/packages/plugins/workflow/src/server/__tests__/triggers/schedule.test.ts @@ -8,6 +8,7 @@ describe.skip('workflow > triggers > schedule', () => { let app: Application; let db: Database; let PostRepo; + let CategoryRepo; let WorkflowModel; let WorkflowRepo; @@ -19,6 +20,7 @@ describe.skip('workflow > triggers > schedule', () => { WorkflowModel = workflow.model; WorkflowRepo = workflow.repository; PostRepo = db.getCollection('posts').repository; + CategoryRepo = db.getCollection('categories').repository; }); afterEach(() => app.destroy()); @@ -231,7 +233,6 @@ describe.skip('workflow > triggers > schedule', () => { await sleep((2.5 - now.getSeconds() % 2) * 1000 - now.getMilliseconds()); const startTime = new Date(); startTime.setMilliseconds(500); - console.log(startTime); const post = await PostRepo.create({ values: { title: 't1' }}); @@ -340,5 +341,31 @@ describe.skip('workflow > triggers > schedule', () => { const d1 = Date.parse(executions[0].context.date); expect(d1 - 1500).toBe(startTime.getTime()); }); + + it('appends', async () => { + const category = await CategoryRepo.create({ values: { name: 'c1' } }); + + const workflow = await WorkflowModel.create({ + enabled: true, + type: 'schedule', + config: { + mode: 1, + collection: 'posts', + startsOn: { + field: 'createdAt', + offset: 2 + }, + appends: ['category'] + } + }); + + const post = await PostRepo.create({ values: { title: 't1', categoryId: category.id } }); + + await sleep(5000); + + const executions = await workflow.getExecutions(); + expect(executions.length).toBe(1); + expect(executions[0].context.data.category.id).toBe(category.id); + }); }); }); diff --git a/packages/plugins/workflow/src/server/instructions/create.ts b/packages/plugins/workflow/src/server/instructions/create.ts index 0eec5550f..420c90db9 100644 --- a/packages/plugins/workflow/src/server/instructions/create.ts +++ b/packages/plugins/workflow/src/server/instructions/create.ts @@ -5,12 +5,12 @@ export default { async run(node: FlowNodeModel, input, processor) { const { collection, - params = {} + params: { appends = [], ...params } = {} } = node.config; - const repo = (node.constructor).database.getRepository(collection); + const { repository, model } = (node.constructor).database.getCollection(collection); const options = processor.getParsedValue(params); - const result = await repo.create({ + const result = await repository.create({ ...options, context: { executionId: processor.execution.id @@ -18,6 +18,18 @@ export default { transaction: processor.transaction }); + if (appends.length) { + const includeFields = appends.filter(field => !result.get(field) || !result[field]); + const included = await model.findByPk(result[model.primaryKeyAttribute], { + attributes: [model.primaryKeyAttribute], + include: includeFields, + transaction: processor.transaction + }); + includeFields.forEach(field => { + result.set(field, included!.get(field), { raw: true }); + }); + } + return { // NOTE: get() for non-proxied instance (#380) result: result.get(), diff --git a/packages/plugins/workflow/src/server/triggers/collection.ts b/packages/plugins/workflow/src/server/triggers/collection.ts index 7563de978..3da113e59 100644 --- a/packages/plugins/workflow/src/server/triggers/collection.ts +++ b/packages/plugins/workflow/src/server/triggers/collection.ts @@ -36,9 +36,10 @@ function getFieldRawName(collection: Collection, name: string) { // async function, should return promise async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: Model, options) { - const { collection: collectionName, condition, changed } = workflow.config; + const { collection: collectionName, condition, changed, mode, appends } = workflow.config; const collection = (data.constructor).database.getCollection(collectionName); const { transaction, context } = options; + const { repository, model } = collection; // NOTE: if no configured fields changed, do not trigger if (changed @@ -53,7 +54,6 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M if (condition && condition.$and?.length) { // TODO: change to map filter format to calculation format // const calculation = toCalculation(condition); - const { repository, model } = collection; const count = await repository.count({ filter: { $and: [ @@ -70,6 +70,18 @@ async function handler(this: CollectionTrigger, workflow: WorkflowModel, data: M } } + if (appends?.length && !(mode & MODE_BITMAP.DESTROY)) { + const includeFields = appends.filter(field => !data.get(field) || !data[field]); + const included = await model.findByPk(data[model.primaryKeyAttribute], { + attributes: [model.primaryKeyAttribute], + include: includeFields, + transaction + }); + includeFields.forEach(field => { + data.set(field, included!.get(field), { raw: true }); + }); + } + this.plugin.trigger(workflow, { data: data.get() }, { context }); diff --git a/packages/plugins/workflow/src/server/triggers/schedule.ts b/packages/plugins/workflow/src/server/triggers/schedule.ts index 37058e1c8..d0e21dc7e 100644 --- a/packages/plugins/workflow/src/server/triggers/schedule.ts +++ b/packages/plugins/workflow/src/server/triggers/schedule.ts @@ -273,7 +273,7 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, { }, async trigger(workflow, now: Date) { - const { startsOn, repeat, endsOn, collection } = workflow.config; + const { startsOn, repeat, endsOn, collection, appends } = workflow.config; const timestamp = now.getTime(); const startTimestamp = getOnTimestampWithOffset(startsOn, now); @@ -333,11 +333,12 @@ ScheduleModes.set(SCHEDULE_MODE.COLLECTION_FIELD, { }); } - const { model } = this.plugin.app.db.getCollection(collection); - const instances = await model.findAll({ - where: { - [Op.and]: conditions - } + const repo = this.plugin.app.db.getRepository(collection); + const instances = await repo.find({ + filter: { + $and: conditions + }, + appends }); instances.forEach(item => {