refactor(plugin-workflow): manual collection block migration (#2064)

* refactor(plugin-workflow): change all collection node initializer in manual config

* refactor(plugin-workflow): clean code

* fix(plugin-workflow): fix custom form field in manual config

* refactor(plugin-workflow): add migration

* fix(plugin-workflow): fix custom form block migration

* fix(plugin-workflow): fix detail read-pretty for association fields
This commit is contained in:
Junyi 2023-06-16 13:06:47 +07:00 committed by GitHub
parent 4faf64f905
commit e5bab3249a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 345 additions and 139 deletions

View File

@ -1,44 +1,71 @@
import React from 'react'; import React from 'react';
import { SchemaInitializer, useCollectionManager } from '@nocobase/client'; import {
CollectionProvider,
SchemaInitializer,
SchemaInitializerItemOptions,
useCollectionManager,
useRecordCollectionDataSourceItems,
useSchemaTemplateManager,
} from '@nocobase/client';
import { traverseSchema } from '../nodes/manual/utils';
export function CollectionBlockInitializer({ insert, collection, dataSource, ...props }) { function InnerCollectionBlockInitializer({ insert, collection, dataSource, ...props }) {
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const { getCollection } = useCollectionManager(); const { getCollection } = useCollectionManager();
const items = useRecordCollectionDataSourceItems('FormItem') as SchemaInitializerItemOptions[];
const resovledCollection = getCollection(collection); const resovledCollection = getCollection(collection);
return (
<SchemaInitializer.Item async function onConfirm({ item }) {
{...props} const template = item.template ? await getTemplateSchemaByMode(item) : null;
onClick={() => { const result = {
insert({
type: 'void', type: 'void',
name: resovledCollection.name, name: resovledCollection.name,
title: resovledCollection.title, title: resovledCollection.title,
'x-decorator': 'CollectionProvider', 'x-decorator': 'DetailsBlockProvider',
'x-decorator-props': { 'x-decorator-props': {
collection, collection,
dataSource,
}, },
'x-component': 'CardItem', 'x-component': 'CardItem',
'x-component-props': { 'x-component-props': {
// title: props.title title: props.title,
}, },
'x-designer': 'SimpleDesigner', 'x-designer': 'SimpleDesigner',
'x-designer-props': {
type: 'record',
},
properties: { properties: {
grid: { grid: {
type: 'void', type: 'void',
'x-decorator': 'Form', 'x-component': 'FormV2',
'x-decorator-props': { 'x-component-props': {
useValues: '{{ useFlowRecordFromBlock }}', useProps: '{{useDetailsBlockProps}}',
}, },
'x-read-pretty': true,
properties: {
grid: template || {
type: 'void',
'x-component': 'Grid', 'x-component': 'Grid',
'x-initializer': 'CollectionFieldInitializers', 'x-initializer': 'ReadPrettyFormItemInitializers',
'x-context-datasource': dataSource, properties: {},
}, },
}, },
},
},
};
traverseSchema(result, (node) => {
if (node['x-uid']) {
delete node['x-uid'];
}
}); });
}} insert(result);
/> }
return <SchemaInitializer.Item {...props} onClick={onConfirm} items={items} />;
}
export function CollectionBlockInitializer(props) {
return (
<CollectionProvider collection={props.collection}>
<InnerCollectionBlockInitializer {...props} />
</CollectionProvider>
); );
} }

View File

@ -1,39 +0,0 @@
import React from 'react';
import { cloneDeep } from 'lodash';
import { SchemaInitializer, useCollection, InitializerWithSwitch, gridRowColWrap } from '@nocobase/client';
function CollectionFieldInitializer({ field, ...props }) {
const uiSchema = cloneDeep(field.uiSchema);
delete uiSchema['x-uid'];
return (
<InitializerWithSwitch
{...props}
schema={{
...uiSchema,
name: field.name,
title: uiSchema.title ?? field.name,
'x-decorator': 'FormItem',
'x-read-pretty': true,
'x-designer': 'FormItem.Designer',
'x-collection-field': field.name,
}}
type="x-collection-field"
/>
);
}
export function CollectionFieldInitializers(props) {
const { fields } = useCollection();
const items = fields
.filter((field) => !['belongsTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.type) && field.uiSchema)
.map((field) => ({
key: field.name,
type: 'item',
title: field.uiSchema.title,
component: CollectionFieldInitializer,
field,
}));
return <SchemaInitializer.Button {...props} items={items} title="{{t('Configure fields')}}" wrap={gridRowColWrap} />;
}

View File

@ -4,7 +4,6 @@ import { appends, collection, values } from '../schemas/collection';
import CollectionFieldset from '../components/CollectionFieldset'; import CollectionFieldset from '../components/CollectionFieldset';
import { NAMESPACE } from '../locale'; import { NAMESPACE } from '../locale';
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer'; import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
import { useCollectionFieldOptions } from '../variable'; import { useCollectionFieldOptions } from '../variable';
import { FieldsSelect } from '../components/FieldsSelect'; import { FieldsSelect } from '../components/FieldsSelect';
@ -63,7 +62,5 @@ export default {
dataSource: `{{$jobsMapByNodeId.${node.id}}}`, dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
}; };
}, },
initializers: { initializers: {},
CollectionFieldInitializers,
},
}; };

View File

@ -0,0 +1,80 @@
import React, { useRef, useMemo, useContext } from 'react';
import { createForm } from '@formily/core';
import { useField } from '@formily/react';
import {
BlockRequestContext,
CollectionProvider,
FormBlockContext,
RecordProvider,
useAPIClient,
useAssociationNames,
} from '@nocobase/client';
import { useFlowContext } from '../../FlowContext';
import { parse } from '@nocobase/utils/client';
function useFlowContextData(dataSource) {
const { execution } = useFlowContext();
const data = useMemo(
() => ({
$context: execution?.context,
$jobsMapByNodeId: (execution?.jobs ?? []).reduce(
(map, job) => Object.assign(map, { [job.nodeId]: job.result }),
{},
),
}),
[execution],
);
const result = useMemo(() => parse(dataSource)(data), [execution]);
return result;
}
export function DetailsBlockProvider(props) {
const field = useField();
const formBlockRef = useRef(null);
const { appends, updateAssociationValues } = useAssociationNames();
const values = useFlowContextData(props.dataSource);
const form = useMemo(
() =>
createForm({
values,
readPretty: true,
}),
[values],
);
const params = {
appends,
};
const service = {
loading: false,
data: {
data: values,
},
};
const api = useAPIClient();
const resource = api.resource(props.collection);
const __parent = useContext(BlockRequestContext);
return (
<CollectionProvider collection={props.collection}>
<RecordProvider record={values} parent={false}>
<BlockRequestContext.Provider value={{ block: 'form', field, service, resource, __parent }}>
<FormBlockContext.Provider
value={{
params,
form,
field,
service,
updateAssociationValues,
formBlockRef,
}}
>
{props.children}
</FormBlockContext.Provider>
</BlockRequestContext.Provider>
</RecordProvider>
</CollectionProvider>
);
}

View File

@ -35,6 +35,9 @@ function InternalFormBlockInitializer({ insert, schema, ...others }) {
useAction: '{{ useSubmit }}', useAction: '{{ useSubmit }}',
}, },
'x-designer': 'Action.Designer', 'x-designer': 'Action.Designer',
'x-designer-props': {
type: 'record',
},
'x-action': `${JOB_STATUS.RESOLVED}`, 'x-action': `${JOB_STATUS.RESOLVED}`,
}, },
}, },

View File

@ -15,6 +15,7 @@ import {
GeneralSchemaDesigner, GeneralSchemaDesigner,
SchemaSettings, SchemaSettings,
useCompile, useCompile,
useFormBlockContext,
} from '@nocobase/client'; } from '@nocobase/client';
import { Registry } from '@nocobase/utils/client'; import { Registry } from '@nocobase/utils/client';
@ -27,6 +28,7 @@ import customForm from './forms/custom';
import createForm from './forms/create'; import createForm from './forms/create';
import updateForm from './forms/update'; import updateForm from './forms/update';
import { FormBlockProvider } from './FormBlockProvider'; import { FormBlockProvider } from './FormBlockProvider';
import { DetailsBlockProvider } from './DetailsBlockProvider';
type ValueOf<T> = T[keyof T]; type ValueOf<T> = T[keyof T];
@ -229,10 +231,6 @@ function useSubmit() {
}; };
} }
function useFlowRecordFromBlock() {
return {};
}
export function SchemaConfig({ value, onChange }) { export function SchemaConfig({ value, onChange }) {
const ctx = useContext(SchemaComponentContext); const ctx = useContext(SchemaComponentContext);
const trigger = useTrigger(); const trigger = useTrigger();
@ -333,6 +331,7 @@ export function SchemaConfig({ value, onChange }) {
{}, {},
), ),
FormBlockProvider, FormBlockProvider,
DetailsBlockProvider,
// NOTE: fake provider component // NOTE: fake provider component
ManualActionStatusProvider(props) { ManualActionStatusProvider(props) {
return props.children; return props.children;
@ -344,7 +343,7 @@ export function SchemaConfig({ value, onChange }) {
}} }}
scope={{ scope={{
useSubmit, useSubmit,
useFlowRecordFromBlock, useDetailsBlockProps: useFormBlockContext,
}} }}
/> />
</SchemaInitializerProvider> </SchemaInitializerProvider>

View File

@ -14,7 +14,6 @@ import {
useCollectionManager, useCollectionManager,
useCurrentUserContext, useCurrentUserContext,
useRecord, useRecord,
useRequest,
useTableBlockContext, useTableBlockContext,
FormBlockContext, FormBlockContext,
useFormBlockContext, useFormBlockContext,
@ -28,6 +27,7 @@ import { instructions, useAvailableUpstreams } from '..';
import { linkNodes } from '../../utils'; import { linkNodes } from '../../utils';
import { manualFormTypes } from './SchemaConfig'; import { manualFormTypes } from './SchemaConfig';
import { FormBlockProvider } from './FormBlockProvider'; import { FormBlockProvider } from './FormBlockProvider';
import { DetailsBlockProvider } from './DetailsBlockProvider';
const nodeCollection = { const nodeCollection = {
title: `{{t("Task", { ns: "${NAMESPACE}" })}}`, title: `{{t("Task", { ns: "${NAMESPACE}" })}}`,
@ -421,23 +421,6 @@ function useSubmit() {
}; };
} }
// parse datasource block from execution context
function useFlowRecordFromBlock(opts) {
const { ['x-context-datasource']: dataSource } = useFieldSchema();
const { execution } = useFlowContext();
const result = parse(dataSource)({
$context: execution?.context,
$jobsMapByNodeId: (execution?.jobs ?? []).reduce(
(map, job) => Object.assign(map, { [job.nodeId]: job.result }),
{},
),
});
return useRequest(() => {
return Promise.resolve({ data: result });
}, opts);
}
function FlowContextProvider(props) { function FlowContextProvider(props) {
const api = useAPIClient(); const api = useAPIClient();
const { id } = useRecord(); const { id } = useRecord();
@ -479,6 +462,7 @@ function FlowContextProvider(props) {
<SchemaComponent <SchemaComponent
components={{ components={{
FormBlockProvider, FormBlockProvider,
DetailsBlockProvider,
ActionBarProvider, ActionBarProvider,
ManualActionStatusProvider, ManualActionStatusProvider,
...Array.from(manualFormTypes.getValues()).reduce( ...Array.from(manualFormTypes.getValues()).reduce(
@ -490,6 +474,7 @@ function FlowContextProvider(props) {
scope={{ scope={{
useSubmit, useSubmit,
useFormBlockProps, useFormBlockProps,
useDetailsBlockProps,
...Array.from(manualFormTypes.getValues()).reduce( ...Array.from(manualFormTypes.getValues()).reduce(
(result, item) => Object.assign(result, item.block.scope), (result, item) => Object.assign(result, item.block.scope),
{}, {},
@ -529,6 +514,11 @@ function useFormBlockProps() {
return { form }; return { form };
} }
function useDetailsBlockProps() {
const { form } = useFormBlockContext();
return { form };
}
function Drawer() { function Drawer() {
const ctx = useContext(SchemaComponentContext); const ctx = useContext(SchemaComponentContext);
const { id, node, workflow, status, updatedAt } = useRecord(); const { id, node, workflow, status, updatedAt } = useRecord();
@ -585,9 +575,6 @@ function Drawer() {
}, },
}, },
}} }}
scope={{
useFlowRecordFromBlock,
}}
/> />
</SchemaComponentContext.Provider> </SchemaComponentContext.Provider>
); );

View File

@ -1,19 +1,22 @@
import React, { useState, useContext } from 'react'; import React, { useState, useContext, useMemo } from 'react';
import { cloneDeep, set } from 'lodash'; import { cloneDeep, set } from 'lodash';
import { Field } from '@formily/core'; import { Field, createForm } from '@formily/core';
import { useForm } from '@formily/react'; import { useForm, useField, useFieldSchema } from '@formily/react';
import { ArrayTable } from '@formily/antd'; import { ArrayTable } from '@formily/antd';
import { import {
ActionContextProvider, ActionContextProvider,
CollectionContext, CollectionContext,
CollectionProvider, CollectionProvider,
FormBlockContext,
gridRowColWrap, gridRowColWrap,
RecordProvider,
SchemaComponent, SchemaComponent,
SchemaInitializer, SchemaInitializer,
SchemaInitializerItemOptions, SchemaInitializerItemOptions,
useCollectionManager, useCollectionManager,
useRecord,
} from '@nocobase/client'; } from '@nocobase/client';
import { merge, uid } from '@nocobase/utils/client'; import { merge, uid } from '@nocobase/utils/client';
@ -22,22 +25,41 @@ import { lang, NAMESPACE } from '../../../locale';
import { findSchema } from '../utils'; import { findSchema } from '../utils';
import { ManualFormType } from '../SchemaConfig'; import { ManualFormType } from '../SchemaConfig';
const FormCollectionContext = React.createContext<any>(null); function CustomFormBlockProvider(props) {
function FormCollectionProvider(props) {
const [fields, setCollectionFields] = useState(props.collection?.fields ?? []); const [fields, setCollectionFields] = useState(props.collection?.fields ?? []);
const userJob = useRecord();
const field = useField();
const fieldSchema = useFieldSchema();
const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {});
const values = userJob?.result?.[formKey];
const form = useMemo(
() =>
createForm({
initialValues: values,
}),
[values],
);
return ( return (
<FormCollectionContext.Provider value={{ setCollectionFields }}>
<CollectionProvider <CollectionProvider
collection={{ collection={{
...props.collection, ...props.collection,
fields, fields,
}} }}
>
<RecordProvider record={values} parent={false}>
<FormBlockContext.Provider
value={{
form,
field,
setCollectionFields,
}}
> >
{props.children} {props.children}
</FormBlockContext.Provider>
</RecordProvider>
</CollectionProvider> </CollectionProvider>
</FormCollectionContext.Provider>
); );
} }
@ -48,7 +70,7 @@ function CustomFormBlockInitializer({ insert, ...props }) {
onClick={() => { onClick={() => {
insert({ insert({
type: 'void', type: 'void',
'x-decorator': 'FormCollectionProvider', 'x-decorator': 'CustomFormBlockProvider',
'x-decorator-props': { 'x-decorator-props': {
collection: { collection: {
name: uid(), name: uid(),
@ -180,7 +202,7 @@ function AddCustomFormField(props) {
const collection = useContext(CollectionContext); const collection = useContext(CollectionContext);
const [interfaceOptions, setInterface] = useState<any>(null); const [interfaceOptions, setInterface] = useState<any>(null);
const [insert, setCallback] = useState<any>(); const [insert, setCallback] = useState<any>();
const { setCollectionFields } = useContext(FormCollectionContext); const { setCollectionFields } = useContext(FormBlockContext);
return ( return (
<AddCustomFormFieldButtonContext.Provider <AddCustomFormFieldButtonContext.Provider
@ -271,7 +293,9 @@ function AddCustomFormField(props) {
type: options.uiSchema.type, type: options.uiSchema.type,
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'CollectionField', 'x-component': 'CollectionField',
'x-interface-options': newField, 'x-component-props': {
field: newField,
},
'x-collection-field': `${collection.name}.${options.name}`, 'x-collection-field': `${collection.name}.${options.name}`,
'x-designer': 'FormItem.Designer', 'x-designer': 'FormItem.Designer',
}); });
@ -329,11 +353,11 @@ export default {
AddCustomFormField, AddCustomFormField,
}, },
components: { components: {
FormCollectionProvider, CustomFormBlockProvider,
}, },
parseFormOptions(root) { parseFormOptions(root) {
const forms = {}; const forms = {};
const formBlocks: any[] = findSchema(root, (item) => item['x-decorator'] === 'FormCollectionProvider'); const formBlocks: any[] = findSchema(root, (item) => item['x-decorator'] === 'CustomFormBlockProvider');
formBlocks.forEach((formBlock) => { formBlocks.forEach((formBlock) => {
const [formKey] = Object.keys(formBlock.properties); const [formKey] = Object.keys(formBlock.properties);
const formSchema = formBlock.properties[formKey]; const formSchema = formBlock.properties[formKey];
@ -342,7 +366,9 @@ export default {
(item) => item['x-component'] === 'CollectionField', (item) => item['x-component'] === 'CollectionField',
true, true,
); );
formBlock['x-decorator-props'].collection.fields = fields.map((field) => field['x-interface-options']); formBlock['x-decorator-props'].collection.fields = fields.map(
(field) => field['x-component-props']?.field ?? field['x-interface-options'],
);
forms[formKey] = { forms[formKey] = {
type: 'custom', type: 'custom',
title: formBlock['x-component-props']?.title || formKey, title: formBlock['x-component-props']?.title || formKey,
@ -358,7 +384,7 @@ export default {
block: { block: {
scope: {}, scope: {},
components: { components: {
FormCollectionProvider: CollectionProvider, CustomFormBlockProvider,
}, },
}, },
} as ManualFormType; } as ManualFormType;

View File

@ -1,8 +1,7 @@
import { BlockInitializers, SchemaInitializerItemOptions, useCollectionManager } from '@nocobase/client'; import { BlockInitializers, SchemaInitializerItemOptions, useCollectionManager } from '@nocobase/client';
import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer'; import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer';
import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers'; import { useCollectionFieldOptions } from '../../variable';
import { filterTypedFields, useCollectionFieldOptions } from '../../variable';
import { NAMESPACE } from '../../locale'; import { NAMESPACE } from '../../locale';
import { SchemaConfig, SchemaConfigButton } from './SchemaConfig'; import { SchemaConfig, SchemaConfigButton } from './SchemaConfig';
import { ModeConfig } from './ModeConfig'; import { ModeConfig } from './ModeConfig';
@ -149,7 +148,5 @@ export default {
} }
: null; : null;
}, },
initializers: { initializers: {},
CollectionFieldInitializers,
},
}; };

View File

@ -3,7 +3,6 @@ import { SchemaInitializerItemOptions, useCollectionDataSource } from '@nocobase
import { appends, collection, filter } from '../schemas/collection'; import { appends, collection, filter } from '../schemas/collection';
import { NAMESPACE } from '../locale'; import { NAMESPACE } from '../locale';
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer'; import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
import { FilterDynamicComponent } from '../components/FilterDynamicComponent'; import { FilterDynamicComponent } from '../components/FilterDynamicComponent';
import { useCollectionFieldOptions } from '../variable'; import { useCollectionFieldOptions } from '../variable';
import { FieldsSelect } from '../components/FieldsSelect'; import { FieldsSelect } from '../components/FieldsSelect';
@ -54,7 +53,7 @@ export default {
return result?.length ? result : null; return result?.length ? result : null;
}, },
useInitializers(node): SchemaInitializerItemOptions | null { useInitializers(node): SchemaInitializerItemOptions | null {
if (!node.config.collection) { if (!node.config.collection || node.config.multiple) {
return null; return null;
} }
@ -66,7 +65,5 @@ export default {
dataSource: `{{$jobsMapByNodeId.${node.id}}}`, dataSource: `{{$jobsMapByNodeId.${node.id}}}`,
}; };
}, },
initializers: { initializers: {},
CollectionFieldInitializers,
},
}; };

View File

@ -3,7 +3,6 @@ import { SchemaInitializerItemOptions, useCollectionDataSource } from '@nocobase
import { appends, collection, filter } from '../schemas/collection'; import { appends, collection, filter } from '../schemas/collection';
import { useCollectionFieldOptions } from '../variable'; import { useCollectionFieldOptions } from '../variable';
import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer'; import { CollectionBlockInitializer } from '../components/CollectionBlockInitializer';
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
import { NAMESPACE, useWorkflowTranslation } from '../locale'; import { NAMESPACE, useWorkflowTranslation } from '../locale';
import { FieldsSelect } from '../components/FieldsSelect'; import { FieldsSelect } from '../components/FieldsSelect';
@ -168,7 +167,5 @@ export default {
dataSource: '{{$context.data}}', dataSource: '{{$context.data}}',
}; };
}, },
initializers: { initializers: {},
CollectionFieldInitializers,
},
}; };

View File

@ -3,7 +3,6 @@ import { useCollectionDataSource, SchemaInitializerItemOptions } from '@nocobase
import { ScheduleConfig } from './ScheduleConfig'; import { ScheduleConfig } from './ScheduleConfig';
import { SCHEDULE_MODE } from './constants'; import { SCHEDULE_MODE } from './constants';
import { NAMESPACE, useWorkflowTranslation } from '../../locale'; import { NAMESPACE, useWorkflowTranslation } from '../../locale';
import { CollectionFieldInitializers } from '../../components/CollectionFieldInitializers';
import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer'; import { CollectionBlockInitializer } from '../../components/CollectionBlockInitializer';
import { useCollectionFieldOptions } from '../../variable'; import { useCollectionFieldOptions } from '../../variable';
import { appends } from '../../schemas/collection'; import { appends } from '../../schemas/collection';
@ -72,7 +71,5 @@ export default {
dataSource: '{{$context.data}}', dataSource: '{{$context.data}}',
}; };
}, },
initializers: { initializers: {},
CollectionFieldInitializers,
},
}; };

View File

@ -0,0 +1,138 @@
import { Migration } from '@nocobase/server';
import { uid } from '@nocobase/utils';
function findSchema(root, filter, onlyLeaf = false) {
const result = [];
if (!root) {
return result;
}
if (filter(root) && (!onlyLeaf || !root.properties)) {
result.push(root);
return result;
}
if (root.properties) {
Object.keys(root.properties).forEach((key) => {
result.push(...findSchema(root.properties[key], filter));
});
}
return result;
}
function migrateSchema(schema = {}): object {
const root = { properties: schema };
const collectionBlocks = findSchema(root, (item) => {
return (
item['x-component'] === 'CardItem' &&
item['x-designer'] === 'SimpleDesigner' &&
item['x-decorator'] === 'CollectionProvider'
);
});
collectionBlocks.forEach((block) => {
const id = uid();
const { grid } = block.properties;
delete block.properties.grid;
const fields = findSchema(grid, (item) => {
return item['x-decorator'] === 'FormItem';
});
fields.forEach((field) => {
Object.assign(field, {
'x-component': 'CollectionField',
'x-collection-field': `${block['x-decorator-props'].collection}.${field['x-collection-field']}`,
});
});
Object.assign(block, {
'x-decorator': 'DetailsBlockProvider',
'x-decorator-props': {
...block['x-decorator-props'],
dataSource: grid['x-context-datasource'],
},
properties: {
[id]: {
type: 'void',
name: id,
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{useDetailsBlockProps}}',
},
properties: {
grid: {
type: 'void',
name: 'grid',
'x-component': 'Grid',
'x-initializer': 'ReadPrettyFormItemInitializers',
properties: grid.properties,
},
},
},
},
});
});
const customForms = findSchema(root, (item) => {
return item['x-decorator'] === 'FormCollectionProvider';
});
customForms.forEach((item) => {
Object.assign(item, {
'x-decorator': 'CustomFormBlockProvider',
});
});
const customFormFields = findSchema(root, (item) => {
return item['x-interface-options'];
});
customFormFields.forEach((field) => {
const options = field['x-interface-options'];
delete field['x-interface-options'];
Object.assign(field, {
'x-component-props': {
field: options,
},
});
});
return schema;
}
export default class extends Migration {
async up() {
const match = await this.app.version.satisfies('<0.9.4-alpha.3');
if (!match) {
return;
}
const { db } = this.context;
const NodeRepo = db.getRepository('flow_nodes');
await db.sequelize.transaction(async (transaction) => {
const nodes = await NodeRepo.find({
filter: {
type: 'manual',
},
transaction,
});
console.log('%d nodes need to be migrated.', nodes.length);
await nodes.reduce(
(promise, node) =>
promise.then(() => {
const { schema, ...config } = node.config;
node.set('config', {
...config,
schema: {
...migrateSchema(schema),
},
});
node.changed('config', true);
return node.save({
silent: true,
transaction,
});
}),
Promise.resolve(),
);
});
}
}