mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-21 13:52:17 +08:00
Merge branch 'next' into develop
This commit is contained in:
@@ -63,7 +63,7 @@ interface Props {
|
||||
expandFlag?: boolean;
|
||||
dragSortBy?: string;
|
||||
association?: string;
|
||||
enableIndexÏColumn?: boolean;
|
||||
enableIndexColumn?: boolean;
|
||||
}
|
||||
|
||||
const InternalTableBlockProvider = (props: Props) => {
|
||||
@@ -77,7 +77,7 @@ const InternalTableBlockProvider = (props: Props) => {
|
||||
fieldNames,
|
||||
collection,
|
||||
association,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
} = props;
|
||||
const field: any = useField();
|
||||
const { resource, service } = useBlockRequestContext();
|
||||
@@ -136,7 +136,7 @@ const InternalTableBlockProvider = (props: Props) => {
|
||||
setExpandFlag: setExpandFlagValue,
|
||||
heightProps,
|
||||
association,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
}),
|
||||
[
|
||||
allIncludesChildren,
|
||||
@@ -153,7 +153,7 @@ const InternalTableBlockProvider = (props: Props) => {
|
||||
setExpandFlagValue,
|
||||
showIndex,
|
||||
association,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ test.describe('where table block can be added', () => {
|
||||
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||
await page.getByRole('menuitem', { name: 'Associated records right' }).waitFor({ state: 'detached' });
|
||||
await page.getByRole('menuitem', { name: 'Table right' }).hover();
|
||||
await page.waitForTimeout(300);
|
||||
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
|
||||
await page.getByRole('menuitem', { name: 'parentAssociationField' }).click();
|
||||
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover();
|
||||
|
||||
+1
@@ -751,6 +751,7 @@ test.describe('actions schema settings', () => {
|
||||
await page
|
||||
.getByRole('button', { name: 'designer-schema-settings-Action.Link-actionSettings:popup-general' })
|
||||
.hover();
|
||||
await page.waitForTimeout(300);
|
||||
};
|
||||
|
||||
test('supported options', async ({ page, mockPage, mockRecord }) => {
|
||||
|
||||
@@ -39,11 +39,11 @@ const enabledIndexColumn: SchemaSettingsItemType = {
|
||||
const { dn } = useDesignable();
|
||||
return {
|
||||
title: t('Enable index column'),
|
||||
checked: field.decoratorProps.enableIndexÏColumn !== false,
|
||||
onChange: async (enableIndexÏColumn) => {
|
||||
checked: field.decoratorProps.enableIndexColumn !== false,
|
||||
onChange: async (enableIndexColumn) => {
|
||||
field.decoratorProps = field.decoratorProps || {};
|
||||
field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn;
|
||||
fieldSchema['x-decorator-props'].enableIndexÏColumn = enableIndexÏColumn;
|
||||
field.decoratorProps.enableIndexColumn = enableIndexColumn;
|
||||
fieldSchema['x-decorator-props'].enableIndexColumn = enableIndexColumn;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
|
||||
+4
-4
@@ -41,11 +41,11 @@ const enabledIndexColumn: SchemaSettingsItemType = {
|
||||
const { dn } = useDesignable();
|
||||
return {
|
||||
title: t('Enable index column'),
|
||||
checked: field.componentProps.enableIndexÏColumn !== false,
|
||||
onChange: async (enableIndexÏColumn) => {
|
||||
checked: field.componentProps.enableIndexColumn !== false,
|
||||
onChange: async (enableIndexColumn) => {
|
||||
field.componentProps = field.componentProps || {};
|
||||
field.componentProps.enableIndexÏColumn = enableIndexÏColumn;
|
||||
fieldSchema['x-component-props'].enableIndexÏColumn = enableIndexÏColumn;
|
||||
field.componentProps.enableIndexColumn = enableIndexColumn;
|
||||
fieldSchema['x-component-props'].enableIndexColumn = enableIndexColumn;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
|
||||
@@ -11,7 +11,7 @@ import { observer, useField, useFieldSchema } from '@formily/react';
|
||||
import { transformMultiColumnToSingleColumn } from '@nocobase/utils/client';
|
||||
import { Select, Space } from 'antd';
|
||||
import { differenceBy, unionBy } from 'lodash';
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
FormProvider,
|
||||
PopupSettingsProvider,
|
||||
@@ -135,6 +135,11 @@ export const InternalPicker = observer(
|
||||
const filter = list.length ? { $and: [{ [`${targetKey}.$ne`]: list }] } : {};
|
||||
return filter;
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!value) {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
}, [value]);
|
||||
const usePickActionProps = () => {
|
||||
const { setVisible } = useActionContext();
|
||||
const { multiple, selectedRows, onChange, options, collectionField } = useContext(RecordPickerContext);
|
||||
|
||||
@@ -107,7 +107,7 @@ export const SubTable: any = observer(
|
||||
const labelUiSchema = useLabelUiSchema(collectionField, fieldNames?.label || 'label');
|
||||
const recordV2 = useCollectionRecord();
|
||||
const collection = useCollection();
|
||||
const { allowSelectExistingRecord, allowAddnew, allowDisassociation, enableIndexÏColumn } = field.componentProps;
|
||||
const { allowSelectExistingRecord, allowAddnew, allowDisassociation, enableIndexColumn } = field.componentProps;
|
||||
|
||||
useSubTableSpecialCase({ rootField: field, rootSchema: schema });
|
||||
|
||||
@@ -263,7 +263,7 @@ export const SubTable: any = observer(
|
||||
locale={{
|
||||
emptyText: <span> {field.editable ? t('Please add or select record') : t('No data')}</span>,
|
||||
}}
|
||||
enableIndexÏColumn={enableIndexÏColumn !== false}
|
||||
enableIndexColumn={enableIndexColumn !== false}
|
||||
footer={() => (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
{field.editable && (
|
||||
|
||||
@@ -673,7 +673,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
onExpand,
|
||||
loading,
|
||||
onClickRow,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
...others
|
||||
} = { ...others1, ...others2 } as any;
|
||||
const field = useArrayField(others);
|
||||
@@ -826,7 +826,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
|
||||
const restProps = useMemo(
|
||||
() => ({
|
||||
rowSelection: enableIndexÏColumn
|
||||
rowSelection: enableIndexColumn
|
||||
? memoizedRowSelection
|
||||
? {
|
||||
type: 'checkbox',
|
||||
@@ -900,7 +900,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
isRowSelect,
|
||||
memoizedRowSelection,
|
||||
paginationProps,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -59,13 +59,19 @@ ReadPretty.Input = (props: InputReadPrettyProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cls(prefixCls, props.className)}
|
||||
style={{ overflowWrap: 'break-word', whiteSpace: 'normal', ...props.style }}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
overflowWrap: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
{props.addonBefore}
|
||||
{props.prefix}
|
||||
{compile(props.addonBefore)}
|
||||
{compile(props.prefix)}
|
||||
{props.ellipsis ? <EllipsisWithTooltip ellipsis={props.ellipsis}>{content}</EllipsisWithTooltip> : content}
|
||||
{props.suffix}
|
||||
{props.addonAfter}
|
||||
{compile(props.suffix)}
|
||||
{compile(props.addonAfter)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -857,7 +857,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
const collection = useCollection();
|
||||
const isTableSelector = schema?.parent?.['x-decorator'] === 'TableSelectorProvider';
|
||||
const ctx = isTableSelector ? useTableSelectorContext() : useTableBlockContext();
|
||||
const { expandFlag, allIncludesChildren, enableIndexÏColumn } = ctx;
|
||||
const { expandFlag, allIncludesChildren, enableIndexColumn } = ctx;
|
||||
const onRowDragEnd = useMemoizedFn(others.onRowDragEnd || (() => {}));
|
||||
const paginationProps = usePaginationProps(pagination1, pagination2, props);
|
||||
const columns = useTableColumns(others, paginationProps);
|
||||
@@ -1023,7 +1023,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
const restProps = useMemo(
|
||||
() => ({
|
||||
rowSelection:
|
||||
enableIndexÏColumn !== false
|
||||
enableIndexColumn !== false
|
||||
? memoizedRowSelection
|
||||
? {
|
||||
type: 'checkbox',
|
||||
@@ -1105,7 +1105,7 @@ export const Table: any = withDynamicSchemaProps(
|
||||
memoizedRowSelection,
|
||||
paginationProps,
|
||||
tableBlockContextBasicValue,
|
||||
enableIndexÏColumn,
|
||||
enableIndexColumn,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -182,10 +182,10 @@ export const TableBlockDesigner = () => {
|
||||
<SchemaSettingsSwitchItem
|
||||
title={t('Enable index column')}
|
||||
checked={field.decoratorProps?.enableSelectColumn !== false}
|
||||
onChange={async (enableIndexÏColumn) => {
|
||||
onChange={async (enableIndexColumn) => {
|
||||
field.decoratorProps = field.decoratorProps || {};
|
||||
field.decoratorProps.enableIndexÏColumn = enableIndexÏColumn;
|
||||
fieldSchema['x-decorator-props'].enableIndexÏColumn = enableIndexÏColumn;
|
||||
field.decoratorProps.enableIndexColumn = enableIndexColumn;
|
||||
fieldSchema['x-decorator-props'].enableIndexColumn = enableIndexColumn;
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
|
||||
+2
-1
@@ -144,7 +144,7 @@ export const DateFilterDynamicComponent = (props) => {
|
||||
<Select
|
||||
key="unit"
|
||||
value={value?.unit}
|
||||
style={{ maxWidth: 140, minWidth: 130 }}
|
||||
style={{ maxWidth: 140 }}
|
||||
onChange={(val) => {
|
||||
const obj = {
|
||||
...value,
|
||||
@@ -158,6 +158,7 @@ export const DateFilterDynamicComponent = (props) => {
|
||||
{ value: 'month', label: t('Calendar Month') },
|
||||
{ value: 'year', label: t('Calendar Year') },
|
||||
]}
|
||||
popupMatchSelectWidth
|
||||
/>,
|
||||
]}
|
||||
{(value?.type === 'exact' || !value?.type) && <SmartDatePicker {...props} />}
|
||||
|
||||
@@ -22,10 +22,14 @@ export default (app: Application) => {
|
||||
|
||||
const Collection = app.db.getCollection('collections');
|
||||
if (Collection) {
|
||||
// @ts-ignore
|
||||
await Collection.repository.setApp(app);
|
||||
// @ts-ignore
|
||||
await Collection.repository.load();
|
||||
}
|
||||
|
||||
app.log.info('syncing database...');
|
||||
|
||||
const force = false;
|
||||
await app.db.sync({
|
||||
force,
|
||||
|
||||
@@ -155,5 +155,5 @@ test('plugin settings permissions', async ({ page, mockPage, mockRole, updateRol
|
||||
await expect(page.getByRole('link', { name: 'Users & Permissions' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Authentication' })).toBeVisible();
|
||||
await page.getByRole('link', { name: 'Users & Permissions' }).click();
|
||||
await expect(page.getByRole('menuitem', { name: 'Data sources' })).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: 'Data sources' }).first()).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
dist
|
||||
@@ -0,0 +1,2 @@
|
||||
/node_modules
|
||||
/src
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './dist/client';
|
||||
export { default } from './dist/client';
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('./dist/client/index.js');
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@nocobase/plugin-text-copy",
|
||||
"version": "1.8.0-alpha.7",
|
||||
"main": "./dist/server/index.js",
|
||||
"displayName": "Text copy",
|
||||
"displayName.zh-CN": "文本复制",
|
||||
"description": "Add copy button to text fields",
|
||||
"description.zh-CN": "为文本字段添加复制按钮",
|
||||
"license": "AGPL-3.0",
|
||||
"homepage": "https://www.nocobase.com",
|
||||
"devDependencies": {
|
||||
"@ant-design/icons": "5.x",
|
||||
"@formily/react": "2.x",
|
||||
"antd": "5.x",
|
||||
"react": "^18.2.0",
|
||||
"react-i18next": "^11.15.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nocobase/client": "1.x",
|
||||
"@nocobase/server": "1.x",
|
||||
"@nocobase/test": "1.x"
|
||||
},
|
||||
"keywords": [
|
||||
"Clipboard",
|
||||
"Copy"
|
||||
],
|
||||
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './dist/server';
|
||||
export { default } from './dist/server';
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('./dist/server/index.js');
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Field } from '@formily/core';
|
||||
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||
import { Plugin, useColumnSchema, useDesignable, useToken } from '@nocobase/client';
|
||||
import { Typography } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { FC, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// Define namespace for i18n
|
||||
const NAMESPACE = 'text-copy';
|
||||
|
||||
const TextCopyButton: FC = observer(
|
||||
() => {
|
||||
const field = useField<Field>();
|
||||
const { token } = useToken();
|
||||
const buttonRef = useRef<HTMLDivElement | null>(null);
|
||||
const [show, setShow] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (field.value && field.readPretty && buttonRef.current) {
|
||||
const currentRef = buttonRef.current;
|
||||
const handleMouseOver = (e: MouseEvent) => {
|
||||
setShow(true);
|
||||
};
|
||||
const handleMouseOut = (e: MouseEvent) => {
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
currentRef?.parentElement?.addEventListener('mouseover', handleMouseOver);
|
||||
currentRef?.parentElement?.addEventListener('mouseout', handleMouseOut);
|
||||
|
||||
return () => {
|
||||
currentRef?.parentElement?.removeEventListener('mouseover', handleMouseOver);
|
||||
currentRef?.parentElement?.removeEventListener('mouseout', handleMouseOut);
|
||||
};
|
||||
}
|
||||
}, [field.readPretty, field.value]);
|
||||
|
||||
const hidden = field.readPretty && (!field.value || !show);
|
||||
|
||||
return (
|
||||
<Typography.Text
|
||||
ref={buttonRef}
|
||||
copyable={{
|
||||
text: field.value,
|
||||
}}
|
||||
style={{ marginLeft: field.readPretty ? token.marginXXS : 0, opacity: hidden ? 0 : 1 }}
|
||||
/>
|
||||
);
|
||||
},
|
||||
{
|
||||
displayName: 'TextCopyButton',
|
||||
},
|
||||
);
|
||||
|
||||
class PluginTextCopy extends Plugin {
|
||||
async load() {
|
||||
const copyButton = <TextCopyButton />;
|
||||
|
||||
this.app.addScopes({
|
||||
TextCopyButton: copyButton,
|
||||
});
|
||||
|
||||
// Add the schema settings to enable/disable the copy functionality
|
||||
this.app.schemaSettingsManager.addItem('fieldSettings:component:Input', 'enableCopier', {
|
||||
type: 'switch',
|
||||
useComponentProps() {
|
||||
const { t } = useTranslation(NAMESPACE);
|
||||
const { fieldSchema: tableFieldSchema } = useColumnSchema();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const { dn } = useDesignable();
|
||||
|
||||
const schema = tableFieldSchema || fieldSchema;
|
||||
|
||||
return {
|
||||
title: t('Display copy button'),
|
||||
checked: !!schema['x-component-props']?.addonAfter,
|
||||
onChange: async (checked) => {
|
||||
if (checked) {
|
||||
field.componentProps.addonAfter = copyButton;
|
||||
_.set(schema, 'x-component-props.addonAfter', '{{TextCopyButton}}');
|
||||
} else {
|
||||
field.componentProps.addonAfter = null;
|
||||
_.unset(schema, 'x-component-props.addonAfter');
|
||||
}
|
||||
|
||||
await dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': schema['x-uid'],
|
||||
'x-component-props': {
|
||||
...schema['x-component-props'],
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginTextCopy;
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export * from './server';
|
||||
export { default } from './server';
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Display copy button": "Display copy button"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Display copy button": "コピーボタンを表示"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Display copy button": "显示复制按钮"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Plugin } from '@nocobase/server';
|
||||
|
||||
export class PluginTextCopyServer extends Plugin {
|
||||
async load() {
|
||||
// Server-side initialization if needed
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginTextCopyServer;
|
||||
@@ -62,7 +62,7 @@ export default class extends Instruction {
|
||||
}
|
||||
|
||||
// NOTE: save job for condition calculation
|
||||
const job = await processor.saveJob({
|
||||
const job = processor.saveJob({
|
||||
status: JOB_STATUS.PENDING,
|
||||
result: { looped },
|
||||
nodeId: node.id,
|
||||
@@ -74,12 +74,17 @@ export default class extends Instruction {
|
||||
const { checkpoint, calculation, expression, continueOnFalse } = node.config.condition ?? {};
|
||||
if ((calculation || expression) && !checkpoint) {
|
||||
const condition = calculateCondition(node, processor);
|
||||
if (!condition && !continueOnFalse) {
|
||||
job.set({
|
||||
status: JOB_STATUS.RESOLVED,
|
||||
result: { looped, broken: true },
|
||||
});
|
||||
return job;
|
||||
if (!condition) {
|
||||
if (continueOnFalse) {
|
||||
job.set('result', { looped: 1 });
|
||||
processor.saveJob(job);
|
||||
} else {
|
||||
job.set({
|
||||
status: JOB_STATUS.RESOLVED,
|
||||
result: { looped, broken: true },
|
||||
});
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +119,7 @@ export default class extends Instruction {
|
||||
(branchJob.status < JOB_STATUS.PENDING && loop.config.exit === EXIT.CONTINUE)
|
||||
) {
|
||||
job.set({ result: { looped: nextIndex } });
|
||||
await processor.saveJob(job);
|
||||
processor.saveJob(job);
|
||||
|
||||
const length = getTargetLength(target);
|
||||
if (nextIndex < length) {
|
||||
@@ -132,7 +137,7 @@ export default class extends Instruction {
|
||||
}
|
||||
}
|
||||
await processor.run(branch, job);
|
||||
return processor.exit();
|
||||
return null;
|
||||
} else {
|
||||
job.set({
|
||||
status: JOB_STATUS.RESOLVED,
|
||||
|
||||
+82
-46
@@ -35,6 +35,7 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
workflow = await WorkflowModel.create({
|
||||
enabled: true,
|
||||
sync: true,
|
||||
type: 'collection',
|
||||
config: {
|
||||
mode: 1,
|
||||
@@ -60,8 +61,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -93,8 +92,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -127,8 +124,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -160,8 +155,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -193,8 +186,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -226,8 +217,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -259,8 +248,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -293,8 +280,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -327,8 +312,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -370,8 +353,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -415,8 +396,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -451,8 +430,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -497,8 +474,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -544,8 +519,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -586,8 +559,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -629,8 +600,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -639,6 +608,84 @@ describe('workflow > instructions > loop', () => {
|
||||
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
||||
expect(jobs[0].result).toEqual({ looped: 1, broken: true });
|
||||
});
|
||||
|
||||
it('continueOnFalse as true', async () => {
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'loop',
|
||||
config: {
|
||||
target: 2,
|
||||
condition: {
|
||||
continueOnFalse: true,
|
||||
calculation: {
|
||||
group: {
|
||||
type: 'and',
|
||||
calculations: [
|
||||
{
|
||||
calculator: 'equal',
|
||||
operands: [1, 0],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const n2 = await workflow.createNode({
|
||||
type: 'echo',
|
||||
upstreamId: n1.id,
|
||||
branchIndex: 0,
|
||||
});
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
|
||||
expect(jobs.length).toBe(2);
|
||||
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
||||
expect(jobs[0].result).toEqual({ looped: 2 });
|
||||
});
|
||||
|
||||
it('continueOnFalse as true, second false', async () => {
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'loop',
|
||||
});
|
||||
await n1.update({
|
||||
config: {
|
||||
target: 2,
|
||||
condition: {
|
||||
continueOnFalse: true,
|
||||
calculation: {
|
||||
group: {
|
||||
type: 'and',
|
||||
calculations: [
|
||||
{
|
||||
calculator: 'equal',
|
||||
operands: [1, '{{$scopes.' + n1.key + '.item}}'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const n2 = await workflow.createNode({
|
||||
type: 'echo',
|
||||
upstreamId: n1.id,
|
||||
branchIndex: 0,
|
||||
});
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
|
||||
expect(jobs.length).toBe(2);
|
||||
expect(jobs[0].status).toBe(JOB_STATUS.RESOLVED);
|
||||
expect(jobs[0].result).toEqual({ looped: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('exit', () => {
|
||||
@@ -665,8 +712,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -700,8 +745,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -735,8 +778,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -770,8 +811,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -782,6 +821,9 @@ describe('workflow > instructions > loop', () => {
|
||||
});
|
||||
|
||||
it('exit as continue with async node inside', async () => {
|
||||
await workflow.update({
|
||||
sync: false,
|
||||
});
|
||||
const n1 = await workflow.createNode({
|
||||
type: 'loop',
|
||||
config: {
|
||||
@@ -863,8 +905,6 @@ describe('workflow > instructions > loop', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [execution] = await workflow.getExecutions();
|
||||
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -967,8 +1007,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
|
||||
@@ -1005,8 +1043,6 @@ describe('workflow > instructions > loop', () => {
|
||||
|
||||
const post = await PostRepo.create({ values: { title: 't1' } });
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const [e1] = await workflow.getExecutions();
|
||||
expect(e1.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
||||
const jobs = await e1.getJobs({ order: [['id', 'ASC']] });
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@nocobase/plugin-file-manager": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-gantt": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-graph-collection-manager": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-text-copy": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-kanban": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-locale-tester": "1.8.0-alpha.7",
|
||||
"@nocobase/plugin-localization": "1.8.0-alpha.7",
|
||||
@@ -117,6 +118,7 @@
|
||||
"@nocobase/plugin-field-sort",
|
||||
"@nocobase/plugin-file-manager",
|
||||
"@nocobase/plugin-gantt",
|
||||
"@nocobase/plugin-text-copy",
|
||||
"@nocobase/plugin-kanban",
|
||||
"@nocobase/plugin-logger",
|
||||
"@nocobase/plugin-notification-manager",
|
||||
|
||||
Reference in New Issue
Block a user