feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { SelectFieldsButton } from './select-fields-button';

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect } from 'react';
import { useNodeTestId, type DatabaseField } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { useToggle } from '@coze-arch/hooks';
import { Dropdown, Tooltip } from '@coze-arch/coze-design';
import { DataTypeTag } from '@/node-registries/common/components';
import { AddButton } from '@/form';
interface SelectFieldsButtonProps {
onSelect?: (id: number) => void;
selectedFieldIDs?: number[];
fields?: DatabaseField[];
filterSystemFields?: boolean;
readonly?: boolean;
testName?: string;
}
const MenuItem = ({
field,
onSelect,
}: {
field: DatabaseField;
onSelect?: (id: number) => void;
}) => (
<Dropdown.Item className="!p-0 m-0">
<Tooltip
className="!translate-x-[-6px]"
content={field.name}
position="left"
>
<div
className="w-[196px] h-[32px] p-[8px] flex items-center justify-between"
onClick={e => {
e.stopPropagation();
onSelect?.(field.id as number);
}}
>
<span className="text-[14px] truncate w-[100px]">{field.name}</span>
<DataTypeTag type={field.type} />
</div>
</Tooltip>
</Dropdown.Item>
);
export function SelectFieldsButton({
onSelect,
selectedFieldIDs = [],
fields = [],
filterSystemFields = true,
readonly = false,
testName,
}: SelectFieldsButtonProps) {
const { state: visible, toggle } = useToggle(false);
const { getNodeSetterId } = useNodeTestId();
const addButtonTestId = getNodeSetterId(
testName || 'select-fileds-add-button',
);
fields = fields?.filter(
({ isSystemField, id }) =>
(!isSystemField || !filterSystemFields) &&
!selectedFieldIDs?.includes(id),
);
const disabled = readonly || !fields || fields.length === 0;
useEffect(() => {
if (disabled && visible) {
toggle();
}
}, [disabled, visible]);
if (disabled) {
return (
<Tooltip
content={I18n.t('workflow_database_no_fields', {}, '没有可添加的字段')}
>
<AddButton disabled={disabled} />
</Tooltip>
);
}
return (
<Dropdown
className="max-h-[260px] overflow-auto"
visible={visible}
trigger="custom"
render={
<Dropdown.Menu>
{fields.map(field => (
<MenuItem field={field} onSelect={onSelect} />
))}
</Dropdown.Menu>
}
position="bottomRight"
onClickOutSide={() => toggle()}
>
<div onClick={() => toggle()}>
<AddButton dataTestId={addButtonTestId} />
</div>
</Dropdown>
);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseCondition, useNodeTestId } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { IconCozPlus } from '@coze-arch/coze-design/icons';
import { Button } from '@coze-arch/coze-design';
import { useFieldArray } from '@/form';
export function ConditionAddButton() {
const { append, readonly } = useFieldArray<DatabaseCondition>();
const { getNodeSetterId } = useNodeTestId();
return (
<Button
disabled={readonly}
className="mt-[4px]"
onClick={() =>
append({ left: undefined, operator: undefined, right: undefined })
}
icon={<IconCozPlus />}
size="small"
color="highlight"
data-testid={getNodeSetterId('condition-add-button')}
>
{I18n.t('workflow_add_condition')}
</Button>
);
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseCondition, ConditionLogic } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import {
type FieldProps,
FieldArray,
Section,
useWatch,
FieldEmpty,
} from '@/form';
import { ConditionLogicField } from './condition-logic-field';
import { ConditionList } from './condition-list';
import { ConditionAddButton } from './condition-add-button';
type ConditionFieldProps = Pick<FieldProps, 'name' | 'label' | 'tooltip'> & {
min?: number;
};
export function ConditionField({
name,
label,
tooltip,
min,
}: ConditionFieldProps) {
const conditionListName = `${name}.conditionList`;
const conditions = useWatch<DatabaseCondition[]>(conditionListName);
return (
<FieldArray name={conditionListName}>
<Section title={label} tooltip={tooltip}>
<div>
<div className="flex">
{conditions?.length > 1 && (
<ConditionLogicField
name={`${name}.logic`}
defaultValue={ConditionLogic.AND}
showStroke={true}
/>
)}
<div className="flex-1 min-w-0">
<ConditionList min={min} />
</div>
</div>
<FieldEmpty
isEmpty={!conditions || conditions.length === 0}
text={I18n.t('workflow_condition_empty')}
/>
<ConditionAddButton />
</div>
</Section>
</FieldArray>
);
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCallback, useEffect, useMemo } from 'react';
import {
type DatabaseCondition,
ViewVariableType,
useNodeTestId,
} from '@coze-workflow/base';
import { useDatabaseNodeService } from '@/hooks';
import { IconRemove, withField, useField, useForm } from '@/form';
import { useConditionLeftDataType } from './use-condition-left-data-type';
import { ConditionRightField } from './condition-right-field';
import { ConditionOperatorField } from './condition-operator-field';
import { ConditionLeftField } from './condition-left-field';
const leftTypeToListTypeMaps = {
[ViewVariableType.String]: ViewVariableType.ArrayString,
[ViewVariableType.Integer]: ViewVariableType.ArrayInteger,
[ViewVariableType.Time]: ViewVariableType.ArrayTime,
[ViewVariableType.Number]: ViewVariableType.ArrayNumber,
[ViewVariableType.Boolean]: ViewVariableType.ArrayBoolean,
};
export const ConditionItemField = withField(
({
disableRemove = false,
onClickRemove,
}: {
disableRemove?: boolean;
onClickRemove?: () => void;
}) => {
const { name, value } = useField<DatabaseCondition>();
const dataType = useConditionLeftDataType();
const operator = value?.operator;
const { getNodeSetterId } = useNodeTestId();
const clearUpRightValueAndOperatorValueOnLeftChange =
useClearUpRightValueAndOperatorValueOnLeftChange();
useCleanupRightValueOnOperatorChange();
// 属于、不属于操作符情况下,右值的类型是左值的数组类型
const rightFieldDataType = useMemo(
() =>
['IN', 'NOT_IN'].includes(operator || '')
? (leftTypeToListTypeMaps?.[dataType as ViewVariableType] ??
ViewVariableType.ArrayString)
: dataType,
[operator, dataType],
);
return (
<div className="flex items-center gap-[4px] min-w-0">
<div className="flex flex-1 items-center gap-[4px] min-w-0">
<div className="w-[42px]">
<ConditionOperatorField
dataType={dataType}
name={`${name}.operator`}
/>
</div>
<div className="flex-1 flex flex-col gap-[4px] min-w-0">
<ConditionLeftField
name={`${name}.left`}
onChange={() => clearUpRightValueAndOperatorValueOnLeftChange()}
/>
<ConditionRightField
operation={value?.operator}
name={`${name}.right`}
dataType={rightFieldDataType}
/>
</div>
</div>
{!disableRemove && (
<IconRemove
onClick={onClickRemove}
testId={`${getNodeSetterId(name)}.remove`}
/>
)}
</div>
);
},
);
function useCleanupRightValueOnOperatorChange() {
const { name, value } = useField<DatabaseCondition>();
const form = useForm();
const databaseNodeService = useDatabaseNodeService();
useEffect(() => {
if (
databaseNodeService.checkConditionOperatorNoNeedRight(value?.operator)
) {
form.setFieldValue(`${name}.right`, undefined);
}
}, [value?.operator]);
}
function useClearUpRightValueAndOperatorValueOnLeftChange() {
const form = useForm();
const { value, name } = useField<DatabaseCondition>();
return useCallback(() => {
form.setFieldValue(`${name}.operator`, undefined);
form.setFieldValue(`${name}.right`, undefined);
}, [form, name, value]);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseConditionLeft, useNodeTestId } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { useCurrentDatabaseQuery } from '@/hooks';
import { withField, Select, useField } from '@/form';
interface ConditionLeftFieldProps {
onChange?: (value: DatabaseConditionLeft) => void;
}
export const ConditionLeftField = withField<ConditionLeftFieldProps>(
({ onChange: propOnChange }) => {
const { name, value, onChange, readonly } =
useField<DatabaseConditionLeft>();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const { getNodeSetterId } = useNodeTestId();
const handleChange = newValue => {
onChange(newValue as DatabaseConditionLeft);
propOnChange?.(newValue as DatabaseConditionLeft);
};
return (
<Select
data-testid={getNodeSetterId(name)}
className="w-full"
value={value}
onChange={handleChange}
disabled={readonly}
placeholder={I18n.t(
'workflow_condition_left_placeholder',
{},
'请选择',
)}
optionList={currentDatabase?.fields?.map(field => ({
label: <span className="max-w-[220px] truncate">{field.name}</span>,
value: field.name,
}))}
/>
);
},
);

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseCondition } from '@coze-workflow/base';
import { useFieldArray, FieldArrayList } from '@/form';
import { ConditionItemField } from './condition-item-field';
interface ConditionListProps {
min?: number;
}
export function ConditionList({ min }: ConditionListProps) {
const { name, value, remove, readonly } = useFieldArray<DatabaseCondition>();
return (
<FieldArrayList>
{value?.map((_, index) => (
<ConditionItemField
name={`${name}.[${index}]`}
disableRemove={
readonly || (min !== undefined ? value?.length <= min : false)
}
onClickRemove={() => {
remove(index);
}}
hasFeedback={false}
/>
))}
</FieldArrayList>
);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type ConditionLogic, useNodeTestId } from '@coze-workflow/base';
import { withField, useField, type FieldProps } from '@/form';
import {
ConditionItemLogic,
type ConditionItemLogicProps,
} from '@/components/condition-item-logic';
export const ConditionLogicField = withField<
Pick<ConditionItemLogicProps, 'showStroke'> & FieldProps
>(({ showStroke }) => {
const { name, value, onChange, readonly } = useField<ConditionLogic>();
const { getNodeSetterId } = useNodeTestId();
return (
<ConditionItemLogic
logic={value}
readonly={readonly}
onChange={onChange}
showStroke={showStroke}
testId={getNodeSetterId(name)}
/>
);
});

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type ViewVariableType,
type DatabaseConditionOperator,
useNodeTestId,
} from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { Tooltip } from '@coze-arch/coze-design';
import { withField, useField, Select } from '@/form';
import { getConditionOperatorOptions } from './get-condition-operator-options';
interface ConditionOperatorFieldProps {
dataType?: ViewVariableType;
}
export const ConditionOperatorField = withField(
({ dataType }: ConditionOperatorFieldProps) => {
const { name, value, onChange, readonly } =
useField<DatabaseConditionOperator>();
const options = getConditionOperatorOptions(dataType);
const { getNodeSetterId } = useNodeTestId();
return (
<Select
className="w-[42px]"
data-testid={getNodeSetterId(name)}
value={value}
disabled={readonly}
onChange={newValue => {
onChange(newValue as DatabaseConditionOperator);
}}
optionList={options}
placeholder={I18n.t('workflow_detail_condition_pleaseselect')}
renderSelectedItem={optionsNode => (
<Tooltip content={optionsNode.label}>
<div className={'flex items-center h-[24px]'}>
{optionsNode.operationIcon}
</div>
</Tooltip>
)}
/>
);
},
);

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type DatabaseConditionRight,
type DatabaseConditionOperator,
type ViewVariableType,
} from '@coze-workflow/base';
import { Input } from '@coze-arch/coze-design';
import { ValueExpressionInput } from '@/nodes-v2/components/value-expression-input';
import { withField, useField } from '@/form';
interface ConditionRightFieldProps {
operation?: DatabaseConditionOperator;
dataType?: ViewVariableType;
}
export const ConditionRightField = withField(
({ operation, dataType }: ConditionRightFieldProps) => {
const { name, value, onChange, readonly } =
useField<DatabaseConditionRight>();
if (operation === 'IS_NULL' || operation === 'IS_NOT_NULL') {
return <Input value={'empty'} disabled size="small" />;
}
if (operation === 'BE_TRUE') {
return <Input value={'true'} disabled size="small" />;
}
if (operation === 'BE_FALSE') {
return <Input value={'false'} disabled size="small" />;
}
return (
<ValueExpressionInput
name={name}
value={value}
inputType={dataType}
readonly={readonly}
onChange={newValue => {
onChange(newValue as DatabaseConditionRight);
}}
/>
);
},
);

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ViewVariableType } from '@coze-workflow/base';
import { type ConditionOperator } from '@coze-workflow/base';
import { ConditionOperatorMap } from '@/constants/condition-operator-map';
export function getConditionOperatorOptions(fieldType?: ViewVariableType) {
let supportedOperators: ConditionOperator[] = [];
if (
fieldType === ViewVariableType.Number ||
fieldType === ViewVariableType.Integer
) {
supportedOperators = [
'EQUAL',
'NOT_EQUAL',
'GREATER_THAN',
'LESS_THAN',
'GREATER_EQUAL',
'LESS_EQUAL',
'IN',
'NOT_IN',
'IS_NULL',
'IS_NOT_NULL',
];
}
if (fieldType === ViewVariableType.String) {
supportedOperators = [
'EQUAL',
'NOT_EQUAL',
'LIKE',
'NOT_LIKE',
'IN',
'NOT_IN',
'IS_NULL',
'IS_NOT_NULL',
];
}
if (fieldType === ViewVariableType.Time) {
supportedOperators = [
'EQUAL',
'NOT_EQUAL',
'GREATER_THAN',
'LESS_THAN',
'GREATER_EQUAL',
'LESS_EQUAL',
'IS_NULL',
'IS_NOT_NULL',
];
}
if (fieldType === ViewVariableType.Boolean) {
supportedOperators = [
'EQUAL',
'NOT_EQUAL',
'IS_NULL',
'IS_NOT_NULL',
'BE_TRUE',
'BE_FALSE',
];
}
return supportedOperators.map(operator => ({
label: ConditionOperatorMap[operator].label,
value: operator,
operationIcon: ConditionOperatorMap[operator].operationIcon,
}));
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ConditionField } from './condition-field';

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseCondition } from '@coze-workflow/base';
import { useCurrentDatabaseQuery } from '@/hooks';
import { useField } from '@/form';
export function useConditionLeftDataType() {
const { value } = useField<DatabaseCondition>();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const field = currentDatabase?.fields?.find(
item => item.name === value?.left,
);
return field?.type;
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useEffect } from 'react';
import { useNodeTestId, type WorkflowDatabase } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { useCurrentDatabaseQuery } from '@/hooks';
import {
DatabaseSelect,
type DatabaseSelectValue,
} from '@/form-extensions/setters/database-select';
import { Section, Field, type FieldProps } from '@/form';
type DatabaseSelectFieldProps = FieldProps<DatabaseSelectValue> & {
afterChange?: (value?: WorkflowDatabase) => void;
};
export const DatabaseSelectField = ({
name,
label,
tooltip,
afterChange,
...rest
}: DatabaseSelectFieldProps) => {
const [changed, setChanged] = useState(false);
const { data: currentDatabase, isLoading } = useCurrentDatabaseQuery();
const { getNodeSetterId } = useNodeTestId();
useEffect(() => {
if (changed && !isLoading) {
afterChange?.(currentDatabase);
setChanged(false);
}
}, [currentDatabase?.id]);
return (
<Section title={I18n.t('workflow_database_node_database_table_title')}>
<Field<DatabaseSelectValue> name={name} {...rest}>
{({ value, onChange, readonly }) => (
<DatabaseSelect
value={value}
readonly={readonly}
onChange={newValue => {
onChange(newValue);
setChanged(true);
}}
addButtonTestID={getNodeSetterId(`${name}.addButton`)}
libraryCardTestID={getNodeSetterId(`${name}.libraryCard`)}
/>
)}
</Field>
</Section>
);
};

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { SelectAndSetFieldsField } from './select-and-set-fields-field';
export { DatabaseSelectField } from './database-select';
export { OutputsField } from './outputs';
export { ConditionField } from './condition-field';

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanoid } from 'nanoid';
import { ViewVariableType } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { OutputsField as BaseOutputsField } from '@/node-registries/common/fields/outputs';
import { type FieldProps } from '@/form';
export function OutputsField({
name,
deps,
}: Pick<FieldProps, 'name' | 'deps'>) {
return (
<BaseOutputsField
title={I18n.t('workflow_detail_node_output')}
tooltip={I18n.t('workflow_240218_08')}
id="database-node-outputs"
name={name}
deps={deps}
topLevelReadonly={true}
disabledTypes={[ViewVariableType.Object]}
defaultValue={[
{
key: nanoid(),
name: 'outputList',
type: ViewVariableType.ArrayObject,
},
{
key: nanoid(),
name: 'rowNum',
type: ViewVariableType.Integer,
},
]}
/>
);
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { SelectAndSetFieldsField } from './select-and-set-fields-field';

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseSettingField } from '@coze-workflow/base';
import { useCurrentDatabaseQuery } from '@/hooks';
import { useFieldArray } from '@/form';
import { SelectFieldsButton } from '../../components';
export function SelectAndSetFieldsAddButton({
afterAppend,
}: {
afterAppend?: () => void;
}) {
const { value, append, readonly } = useFieldArray<DatabaseSettingField>();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const selectedFieldIDs = value?.map(({ fieldID }) => fieldID);
return (
<SelectFieldsButton
readonly={readonly}
selectedFieldIDs={selectedFieldIDs}
onSelect={id => {
append({ fieldID: id });
afterAppend?.();
}}
fields={currentDatabase?.fields?.filter(
({ isSystemField }) => !isSystemField,
)}
/>
);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { ColumnTitles } from '@/form';
export function SelectAndSetFieldsColumnsTitle() {
return (
<ColumnTitles
columns={[
{
label: I18n.t('workflow_detail_node_parameter_name'),
style: { width: 148 },
},
{ label: I18n.t('workflow_detail_end_output_value') },
]}
/>
);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createContext, useContext } from 'react';
import { type DatabaseField } from '@coze-workflow/base';
interface SelectAndSetFieldsFieldContextProps {
shouldDisableRemove?: (field?: DatabaseField) => boolean;
}
export const SelectAndSetFieldsFieldContext =
createContext<SelectAndSetFieldsFieldContextProps>({});
export const useSelectAndSetFieldsContext = () =>
useContext(SelectAndSetFieldsFieldContext);

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseField } from '@coze-workflow/base';
import { withFieldArray, type FieldProps } from '@/form';
import { SelectAndSetFieldsFieldContext } from './select-and-set-fields-context';
import { SelectAndSetFields } from './select-and-set-fields';
interface SelectAndSetFieldsFieldProps extends Pick<FieldProps, 'name'> {
shouldDisableRemove?: (field?: DatabaseField) => boolean;
}
export const SelectAndSetFieldsField = withFieldArray<
SelectAndSetFieldsFieldProps,
DatabaseField
>(({ shouldDisableRemove = () => false }) => (
<SelectAndSetFieldsFieldContext.Provider
value={{
shouldDisableRemove,
}}
>
<SelectAndSetFields />
</SelectAndSetFieldsFieldContext.Provider>
));

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseSettingField } from '@coze-workflow/base';
import { ValueExpressionInputField } from '@/node-registries/common/fields';
import { DataTypeTag } from '@/node-registries/common/components';
import { useCurrentDatabaseQuery } from '@/hooks';
import { FieldArrayItem, useFieldArray } from '@/form';
import { useSelectAndSetFieldsContext } from './select-and-set-fields-context';
export function SelectAndSetFieldsItem({ index }: { index: number }) {
const { name, value, remove, readonly } =
useFieldArray<DatabaseSettingField>();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const { shouldDisableRemove } = useSelectAndSetFieldsContext();
const fieldSchema = value?.[index];
const field = currentDatabase?.fields?.find(
({ id }) => id === fieldSchema?.fieldID,
);
return (
<FieldArrayItem
onRemove={() => remove(index)}
disableRemove={readonly || shouldDisableRemove?.(field)}
>
<ValueExpressionInputField
label={field?.name}
required={field?.required}
tooltip={field?.description}
labelExtra={field?.type && <DataTypeTag type={field.type} />}
name={`${name}.${index}.fieldValue`}
inputType={field?.type}
/>
</FieldArrayItem>
);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type DatabaseSettingField } from '@coze-workflow/base';
import { FieldArrayList, useFieldArray } from '@/form';
import { SelectAndSetFieldsItem } from './select-and-set-fields-item';
export function SelectAndSetFieldsList() {
const { value } = useFieldArray<DatabaseSettingField>();
return (
<FieldArrayList>
{value?.map(({ fieldID }, index) => (
<SelectAndSetFieldsItem key={fieldID} index={index} />
))}
</FieldArrayList>
);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type PropsWithChildren } from 'react';
import { type DatabaseSettingField } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { Section, useFieldArray, useSectionRef } from '@/form';
import { SelectAndSetFieldsAddButton } from './select-and-set-fields-add-button';
export function SelectAndSetFieldsSection({ children }: PropsWithChildren) {
const { value } = useFieldArray<DatabaseSettingField>();
const sectionRef = useSectionRef();
return (
<Section
ref={sectionRef}
title={I18n.t('workflow_select_and_set_fields_title')}
isEmpty={!value || value?.length === 0}
emptyText={I18n.t('workflow_select_and_set_fields_empty')}
actions={[
<SelectAndSetFieldsAddButton
afterAppend={() => sectionRef.current?.open()}
/>,
]}
>
{children}
</Section>
);
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SelectAndSetFieldsSection } from './select-and-set-fields-section';
import { SelectAndSetFieldsList } from './select-and-set-fields-list';
import { SelectAndSetFieldsColumnsTitle } from './select-and-set-fields-columns-title';
export const SelectAndSetFields = () => (
<SelectAndSetFieldsSection>
<SelectAndSetFieldsColumnsTitle />
<SelectAndSetFieldsList />
</SelectAndSetFieldsSection>
);

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { useCurrentDatabaseField } from './use-current-database-field';
export { useResetSelectAndSetFields } from './use-reset-select-and-set-fields';
export { useResetCondition } from './use-reset-condition';

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCurrentDatabaseQuery } from '@/hooks';
export function useCurrentDatabaseField(fieldID?: number) {
const { data: currentDatabase } = useCurrentDatabaseQuery();
return currentDatabase?.fields?.find(item => item.id === fieldID);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConditionLogic } from '@coze-workflow/base';
import { useCurrentDatabaseQuery } from '@/hooks';
import { useForm } from '@/form';
export function useResetCondition(conditionFieldName: string) {
const form = useForm();
const { data: currentDatabase } = useCurrentDatabaseQuery();
return () => {
// 当前有选中的数据库 需要有一个空条件
if (currentDatabase) {
form.setFieldValue(conditionFieldName, {
conditionList: [
{ left: undefined, operator: undefined, right: undefined },
],
logic: ConditionLogic.AND,
});
} else {
form.setFieldValue(conditionFieldName, undefined);
}
};
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCurrentDatabaseQuery } from '@/hooks';
import { useForm } from '@/form';
export function useResetSelectAndSetFields(name: string) {
const { data: currentDatabase } = useCurrentDatabaseQuery();
const form = useForm();
return () => {
const newSelectFieldSchemas =
currentDatabase?.fields
?.filter(({ required }) => required)
?.map(({ id }) => ({
fieldID: id,
})) || [];
form.setFieldValue(name, newSelectFieldSchemas);
};
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanoid } from 'nanoid';
import { ViewVariableType } from '@coze-workflow/base';
export function getOutputsDefaultValue() {
return [
{
key: nanoid(),
name: 'outputList',
type: ViewVariableType.ArrayObject,
},
{
key: nanoid(),
name: 'rowNum',
type: ViewVariableType.Integer,
},
];
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { getOutputsDefaultValue } from './get-outputs-default-value';

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WorkflowNode } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { DatabaseNodeService, ValueExpressionService } from '@/services';
export const createConditionValidator = (conditionFieldPath: string) => ({
[`${conditionFieldPath}.*.left`]: ({ value }) => {
if (value === undefined) {
return I18n.t('workflow_detail_node_error_empty');
}
},
[`${conditionFieldPath}.*.operator`]: ({ value }) => {
if (value === undefined) {
return I18n.t('workflow_detail_condition_condition_empty');
}
},
[`${conditionFieldPath}.*.right`]: ({ name, value, context }) => {
const node = new WorkflowNode(context.node);
const conditionPathName = name.replace('.right', '');
const condition = node.getValueByPath(conditionPathName);
const databaseNodeService = context.node.getService(DatabaseNodeService);
const valueExpressionService = context.node.getService(
ValueExpressionService,
);
// 如果是不需要右值就跳过校验
if (
databaseNodeService.checkConditionOperatorNoNeedRight(condition?.operator)
) {
return;
}
if (value === undefined) {
return I18n.t('workflow_detail_node_error_empty');
}
// 检验引用变量被删除的情况
if (
valueExpressionService.isRefExpression(value) &&
!valueExpressionService.isRefExpressionVariableExists(value, context.node)
) {
return I18n.t('workflow_detail_variable_referenced_error');
}
},
});

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { databaseSelectFieldName } from '@/constants/database-field-names';
export const createDatabaseValidator = () => ({
[databaseSelectFieldName]: ({ value }) => {
if (!value || value.length === 0) {
return I18n.t('workflow_detail_node_error_empty');
}
},
});

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { ValueExpressionService } from '@/services';
export function createSelectAndSetFieldsValidator() {
return {
['inputs.*.fieldInfo.*.fieldValue']: ({ value, context }) => {
const valueExpressionService = context.node.getService(
ValueExpressionService,
);
// 检查引用变量是否被删除
if (
valueExpressionService.isRefExpression(value) &&
!valueExpressionService.isRefExpressionVariableExists(
value,
context.node,
)
) {
return I18n.t('workflow_detail_variable_referenced_error');
}
},
};
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createConditionValidator } from './create-condition-validator';
export { createDatabaseValidator } from './create-database-validator';
export { createSelectAndSetFieldsValidator } from './create-select-and-set-fields-validator';

View File

@@ -0,0 +1,9 @@
.container {
position: relative;
}
.auto-generate {
position: absolute;
top: -32px;
right: 0;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useState } from 'react';
import { I18n } from '@coze-arch/i18n';
import { ExpressionEditor } from '@/nodes-v2/components/expression-editor';
import { AutoGenerate } from '@/form-extensions/setters/sql/sql/auto-generate';
import { useField, withField } from '@/form';
import styles from './index.module.less';
const Sql = () => {
const { value, onChange, readonly, errors } = useField<string>();
const [key, setKey] = useState<number>(0);
function handleSubmit(newValue) {
onChange(newValue);
setKey(key + 1);
}
return (
<div className={styles.container}>
{/* The community version does not currently support the AI-generated SQL function for future expansion */}
{!readonly && !IS_OPEN_SOURCE ? (
<AutoGenerate
className={styles['auto-generate']}
onSubmit={handleSubmit}
/>
) : null}
<ExpressionEditor
key={key.toString()}
value={value}
onChange={e => onChange(e)}
readonly={readonly}
isError={Boolean(errors?.length)}
placeholder={I18n.t('workflow_240218_12')}
name={'/sql'}
/>
</div>
);
};
export const SqlField = withField(Sql);

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function transformOnInit(value) {
if (!value) {
return;
}
const { inputs = {}, nodeMeta, outputs } = value;
return {
nodeMeta,
...inputs,
outputs,
};
}
export function transformOnSubmit(value) {
const { nodeMeta, outputs, ...inputs } = value;
return {
nodeMeta,
inputs,
outputs,
};
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { get } from 'lodash-es';
import { I18n } from '@coze-arch/i18n';
import {
ValidateTrigger,
type WorkflowNodeRegistry,
} from '@flowgram-adapter/free-layout-editor';
import { provideNodeOutputVariablesEffect } from '@/nodes-v2/materials/provide-node-output-variables';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import { createValueExpressionInputValidate } from '@/nodes-v2/materials/create-value-expression-input-validate';
import { createNodeInputNameValidate } from '@/nodes-v2/components/node-input-name/validate';
import { getOutputsDefaultValue } from '@/node-registries/database/common/utils';
import FormRender from './form';
import { transformOnInit, transformOnSubmit } from './data-transformer';
export const DATABASE_NODE_FORM_META: WorkflowNodeRegistry['formMeta'] = {
// 节点表单渲染
render: () => <FormRender />,
// 验证触发时机
validateTrigger: ValidateTrigger.onChange,
// 验证规则
validate: {
nodeMeta: nodeMetaValidate,
'inputParameters.*.name': createNodeInputNameValidate({
getNames: ({ formValues }) =>
(get(formValues, 'inputParameters') || []).map(item => item.name),
}),
'inputParameters.*.input': createValueExpressionInputValidate({
required: true,
}),
sql: ({ value }) =>
!value ? I18n.t('workflow_detail_node_error_empty') : undefined,
databaseInfoList: ({ value }) => {
if (!value || value.length === 0) {
return I18n.t('workflow_detail_node_error_empty');
}
},
},
// 默认值
defaultValues: {
inputParameters: [{ name: 'input' }],
databaseInfoList: [],
outputs: getOutputsDefaultValue(),
},
// 副作用
effect: {
outputs: provideNodeOutputVariablesEffect,
},
// 初始化数据转换
formatOnInit: transformOnInit,
// 提交数据转换
formatOnSubmit: transformOnSubmit,
};

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useNodeTestId } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import {
DatabaseSelectField,
OutputsField,
} from '@/node-registries/database/common/fields';
import { NodeConfigForm } from '@/node-registries/common/components';
import { Section } from '@/form';
import { InputsParametersField } from '../../common/fields';
import { SqlField } from './components/sql-field';
const Render = () => {
const { getNodeSetterId } = useNodeTestId();
const setterTestId = getNodeSetterId('');
return (
<NodeConfigForm>
<InputsParametersField
testId={setterTestId}
name="inputParameters"
tooltip={I18n.t(
'workflow_240218_07',
{},
'需要添加的输入变量SQL中可直接引用此处添加的变量',
)}
/>
<DatabaseSelectField name="databaseInfoList" />
<Section
title={I18n.t('workflow_240218_09', {}, 'SQL')}
tooltip={I18n.t(
'workflow_240218_10',
{},
'要执行的SQL语句,可以直接使用输入参数中的变量,注意rowNum输出返回的行数或者受影响的行数,outputList中的变量名需与SQL中定义的字段名一致。',
)}
>
<SqlField name="sql" />
</Section>
<OutputsField name="outputs" />
</NodeConfigForm>
);
};
export default Render;

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DATABASE_NODE_REGISTRY } from './node-registry';

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
DEFAULT_NODE_META_PATH,
DEFAULT_NODE_SIZE,
type WorkflowNodeRegistry,
} from '@coze-workflow/nodes';
import { StandardNodeType } from '@coze-workflow/base';
import { type NodeTestMeta } from '@/test-run-kit';
import { test } from './node-test';
import { DATABASE_NODE_FORM_META } from './form-meta';
export const DATABASE_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> = {
type: StandardNodeType.Database,
meta: {
nodeDTOType: StandardNodeType.Database,
style: {
width: 484,
},
size: DEFAULT_NODE_SIZE,
nodeMetaPath: DEFAULT_NODE_META_PATH,
test,
helpLink: '/open/docs/guides/database_sql_node',
},
formMeta: DATABASE_NODE_FORM_META,
};

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { generateParametersToProperties } from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const inputParameters = formData?.inputParameters;
return generateParametersToProperties(inputParameters, { node });
},
};

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ValidateTrigger } from '@flowgram-adapter/free-layout-editor';
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { DatabaseNodeService } from '@/services/database-node-service';
import { provideNodeOutputVariablesEffect } from '@/nodes-v2/materials/provide-node-output-variables';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import {
createDatabaseValidator,
createSelectAndSetFieldsValidator,
} from '@/node-registries/database/common/validators';
import { getOutputsDefaultValue } from '@/node-registries/database/common/utils';
import { createSelectAndSetFieldsFieldName } from '@/constants/database-field-names';
import { DatabaseCreateForm } from './database-create-form';
export const DatabaseCreateFormMeta: WorkflowNodeRegistry['formMeta'] = {
render: () => <DatabaseCreateForm />,
validateTrigger: ValidateTrigger.onChange,
validate: {
nodeMeta: nodeMetaValidate,
...createDatabaseValidator(),
...createSelectAndSetFieldsValidator(),
},
defaultValues: {
inputs: {
databaseInfoList: [],
},
outputs: getOutputsDefaultValue(),
},
formatOnInit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertSettingFieldDTOToField(
createSelectAndSetFieldsFieldName,
value,
);
return value;
},
formatOnSubmit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertSettingFieldToDTO(
createSelectAndSetFieldsFieldName,
value,
context.node,
);
return value;
},
effect: {
outputs: provideNodeOutputVariablesEffect,
},
};

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useResetSelectAndSetFields } from '@/node-registries/database/common/hooks';
import {
SelectAndSetFieldsField,
DatabaseSelectField,
OutputsField,
} from '@/node-registries/database/common/fields';
import { withNodeConfigForm } from '@/node-registries/common/hocs';
import { useCurrentDatabaseQuery } from '@/hooks';
import {
createSelectAndSetFieldsFieldName,
databaseSelectFieldName,
} from '@/constants/database-field-names';
export const DatabaseCreateForm: React.FC = withNodeConfigForm(() => {
const { data: currentDatabase } = useCurrentDatabaseQuery();
const resetSelectAndSetFields = useResetSelectAndSetFields(
createSelectAndSetFieldsFieldName,
);
return (
<>
<DatabaseSelectField
name={databaseSelectFieldName}
afterChange={() => {
resetSelectAndSetFields();
}}
/>
{currentDatabase ? (
<SelectAndSetFieldsField
name={createSelectAndSetFieldsFieldName}
shouldDisableRemove={field => field?.required ?? false}
/>
) : null}
<OutputsField name="outputs" />
</>
);
});

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { get } from 'lodash-es';
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { StandardNodeType } from '@coze-workflow/base';
import { type WorkflowPlaygroundContext } from '@/workflow-playground-context';
import { type NodeTestMeta } from '@/test-run-kit';
import { DatabaseNodeService } from '@/services';
import { test } from './node-test';
import { DatabaseCreateFormMeta } from './database-create-form-meta';
export const DATABASE_CREATE_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> =
{
type: StandardNodeType.DatabaseCreate,
meta: {
nodeDTOType: StandardNodeType.DatabaseCreate,
test,
helpLink: '/open/docs/guides/database_insert_node',
},
formMeta: DatabaseCreateFormMeta,
onInit: async (nodeJson, context: WorkflowPlaygroundContext) => {
if (!nodeJson) {
return;
}
const databaseNodeService =
context.entityManager.getService<DatabaseNodeService>(
DatabaseNodeService,
);
const databaseId =
nodeJson?.data.inputs.databaseInfoList?.[0]?.databaseInfoID ?? '';
if (!databaseId) {
return;
}
await databaseNodeService.load(databaseId);
},
onDispose: (nodeJson, context: WorkflowPlaygroundContext) => {
if (!nodeJson) {
return;
}
const databaseNodeService =
context.entityManager.getService<DatabaseNodeService>(
DatabaseNodeService,
);
const databaseId =
get(nodeJson, 'inputs.databaseInfoList[0].databaseInfoID') ?? '';
if (!databaseId) {
return;
}
databaseNodeService.clearDatabaseError(databaseId);
},
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DATABASE_CREATE_NODE_REGISTRY } from './database-create-node-registry';

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MemoryApi } from '@coze-arch/bot-api';
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { generateParametersToProperties } from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
async generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const databaseID = formData?.inputs?.databaseInfoList[0]?.databaseInfoID;
if (!databaseID) {
return {};
}
const db = await MemoryApi.GetDatabaseByID({
id: databaseID,
need_sys_fields: true,
});
const fieldInfo = formData?.inputs?.insertParam?.fieldInfo ?? [];
const parameters = fieldInfo.map(item => {
const databaseField = db?.database_info?.field_list?.find(
field => field.alterId === item.fieldID,
);
return {
name: `__setting_field_${item?.fieldID}`,
title: databaseField?.name,
input: item?.fieldValue,
};
});
return generateParametersToProperties(parameters, { node });
},
};

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ValidateTrigger } from '@flowgram-adapter/free-layout-editor';
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { DatabaseNodeService } from '@/services';
import { provideNodeOutputVariablesEffect } from '@/nodes-v2/materials/provide-node-output-variables';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import {
createConditionValidator,
createDatabaseValidator,
} from '@/node-registries/database/common/validators';
import { getOutputsDefaultValue } from '@/node-registries/database/common/utils';
import {
deleteConditionListFieldName,
deleteConditionLogicFieldName,
} from '@/constants/database-field-names';
import { DatabaseDeleteForm } from './database-delete-form';
export const DatabaseDeleteFormMeta: WorkflowNodeRegistry['formMeta'] = {
render: () => <DatabaseDeleteForm />,
validateTrigger: ValidateTrigger.onChange,
validate: {
nodeMeta: nodeMetaValidate,
...createConditionValidator(deleteConditionListFieldName),
...createDatabaseValidator(),
},
defaultValues: {
inputs: {
databaseInfoList: [],
},
outputs: getOutputsDefaultValue(),
},
effect: {
outputs: provideNodeOutputVariablesEffect,
},
formatOnInit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertConditionDTOToCondition(
deleteConditionListFieldName,
value,
);
value = databaseNodeService.convertConditionLogicDTOToConditionLogic(
deleteConditionLogicFieldName,
value,
);
return value;
},
formatOnSubmit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertConditionToDTO(
deleteConditionListFieldName,
value,
context.node,
);
value = databaseNodeService.convertConditionLogicToConditionLogicDTO(
deleteConditionLogicFieldName,
value,
);
return value;
},
};

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { useResetCondition } from '@/node-registries/database/common/hooks';
import {
DatabaseSelectField,
OutputsField,
ConditionField,
} from '@/node-registries/database/common/fields';
import { withNodeConfigForm } from '@/node-registries/common/hocs';
import { useCurrentDatabaseQuery } from '@/hooks';
import {
databaseSelectFieldName,
deleteConditionFieldName,
} from '@/constants/database-field-names';
export const DatabaseDeleteForm: React.FC = withNodeConfigForm(() => {
const { data: currentDatabase } = useCurrentDatabaseQuery();
const resetCondition = useResetCondition(deleteConditionFieldName);
return (
<>
<DatabaseSelectField
name={databaseSelectFieldName}
afterChange={() => {
resetCondition();
}}
/>
{currentDatabase ? (
<ConditionField
label={I18n.t('workflow_delete_conditon_title')}
name={deleteConditionFieldName}
min={1}
/>
) : null}
<OutputsField name="outputs" />
</>
);
});

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { StandardNodeType } from '@coze-workflow/base';
import { type NodeTestMeta } from '@/test-run-kit';
import { test } from './node-test';
import { DatabaseDeleteFormMeta } from './database-delete-form-meta';
export const DATABASE_DELETE_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> =
{
type: StandardNodeType.DatabaseDelete,
meta: {
nodeDTOType: StandardNodeType.DatabaseDelete,
test,
helpLink: '/open/docs/guides/database_delete_node',
},
formMeta: DatabaseDeleteFormMeta,
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DATABASE_DELETE_NODE_REGISTRY } from './database-delete-node-registry';

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { generateParametersToProperties } from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const conditionList = (
formData?.inputs?.deleteParam?.condition?.conditionList ?? []
).map((item, idx) => {
const { left, right } = item;
const name = left;
const rightValue = right;
return {
name: `__condition_right_${idx}`,
title: `${name}`,
input: rightValue,
};
});
return generateParametersToProperties(conditionList, { node });
},
};

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ValidateTrigger } from '@flowgram-adapter/free-layout-editor';
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { I18n } from '@coze-arch/i18n';
import { DatabaseNodeService } from '@/services';
import { provideNodeOutputVariablesEffect } from '@/nodes-v2/materials/provide-node-output-variables';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import {
createConditionValidator,
createDatabaseValidator,
} from '@/node-registries/database/common/validators';
import { getOutputsDefaultValue } from '@/node-registries/database/common/utils';
import {
queryConditionListFieldName,
queryLimitFieldName,
queryConditionLogicFieldName,
} from '@/constants/database-field-names';
import { DatabaseQueryForm } from './database-query-form';
export const DatabaseQueryFormMeta: WorkflowNodeRegistry['formMeta'] = {
render: () => <DatabaseQueryForm />,
validateTrigger: ValidateTrigger.onChange,
validate: {
nodeMeta: nodeMetaValidate,
[queryLimitFieldName]: ({ value }) => {
if (value < 1 || value > 1000) {
return I18n.t('workflow_database_query_limit');
}
},
...createConditionValidator(queryConditionListFieldName),
...createDatabaseValidator(),
},
defaultValues: {
inputs: {
databaseInfoList: [],
},
outputs: getOutputsDefaultValue(),
},
effect: {
outputs: provideNodeOutputVariablesEffect,
},
formatOnInit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertConditionDTOToCondition(
queryConditionListFieldName,
value,
);
value = databaseNodeService.convertConditionLogicDTOToConditionLogic(
queryConditionLogicFieldName,
value,
);
return value;
},
formatOnSubmit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertConditionToDTO(
queryConditionListFieldName,
value,
context.node,
);
value = databaseNodeService.convertConditionLogicToConditionLogicDTO(
queryConditionLogicFieldName,
value,
);
return value;
},
};

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import {
DatabaseSelectField,
ConditionField,
OutputsField,
} from '@/node-registries/database/common/fields';
import { withNodeConfigForm } from '@/node-registries/common/hocs';
import { useCurrentDatabaseQuery } from '@/hooks';
import {
databaseSelectFieldName,
queryFieldsFieldName,
queryConditionFieldName,
orderByFieldName,
queryLimitFieldName,
} from '@/constants/database-field-names';
import { useResetFields } from './use-reset-fields';
import { QueryLimitField } from './query-limit-field';
import { QueryFieldsField } from './query-fields-field';
import { OrderByField } from './order-by-field';
export const DatabaseQueryForm: React.FC = withNodeConfigForm(() => {
const { data: currentDatabase } = useCurrentDatabaseQuery();
const resetFields = useResetFields();
return (
<>
<DatabaseSelectField
name={databaseSelectFieldName}
afterChange={resetFields}
/>
{currentDatabase ? (
<QueryFieldsField name={queryFieldsFieldName} />
) : null}
{currentDatabase ? (
<ConditionField
label={I18n.t('workflow_query_condition_title')}
name={queryConditionFieldName}
/>
) : null}
{currentDatabase ? <OrderByField name={orderByFieldName} /> : null}
{currentDatabase ? <QueryLimitField name={queryLimitFieldName} /> : null}
<OutputsField deps={[queryFieldsFieldName]} name="outputs" />
</>
);
});

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { StandardNodeType } from '@coze-workflow/base';
import { type NodeTestMeta } from '@/test-run-kit';
import { test } from './node-test';
import { DatabaseQueryFormMeta } from './database-query-form-meta';
export const DATABASE_QUERY_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> =
{
type: StandardNodeType.DatabaseQuery,
meta: {
nodeDTOType: StandardNodeType.DatabaseQuery,
test,
helpLink: '/open/docs/guides/database_select_node',
},
formMeta: DatabaseQueryFormMeta,
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DATABASE_QUERY_NODE_REGISTRY } from './database-query-node-registry';

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { generateParametersToProperties } from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const conditionList = (
formData?.inputs?.selectParam?.condition?.conditionList ?? []
).map((item, idx) => {
const { left, right } = item;
const name = left;
const rightValue = right;
return {
name: `__condition_right_${idx}`,
title: `${name}`,
input: rightValue,
};
});
return generateParametersToProperties(conditionList, { node });
},
};

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ViewVariableType, useNodeTestId } from '@coze-workflow/base';
import { SingleSelect } from '@coze-arch/coze-design';
import { withField, useField } from '@/form';
interface AceOrDescProps {
type?: ViewVariableType;
}
const AceOrDescLabelMap = {
[ViewVariableType.String]: ['A → Z', 'Z → A'],
[ViewVariableType.Integer]: ['0 → 9', '9 → 0'],
[ViewVariableType.Number]: ['0 → 9', '9 → 0'],
[ViewVariableType.Boolean]: ['0 → 1', '1 → 0'],
[ViewVariableType.Time]: ['0 → 9', '9 → 0'],
};
export const AceOrDescField = withField<AceOrDescProps>(({ type }) => {
const { name, value, onChange, readonly } = useField<boolean>();
const { getNodeSetterId } = useNodeTestId();
const [aceLabel, descLabel] =
type === undefined ? [] : AceOrDescLabelMap[type];
return (
<SingleSelect
layout="hug"
disabled={readonly}
value={`${value}`}
onChange={e => onChange(e.target.value === 'true')}
options={[
{
label: aceLabel,
value: 'true',
},
{
label: descLabel,
value: 'false',
},
]}
size="small"
data-testid={getNodeSetterId(name)}
/>
);
});

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { OrderByField } from './order-by-field';

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SelectFieldsButton } from '@/node-registries/database/common/components';
import { useFieldArray } from '@/form';
import { useQueryFields } from './use-query-fields';
import { type OrderByFieldSchema } from './types';
interface OrderByAddButtonProps {
afterAppend?: () => void;
}
export function OrderByAddButton({ afterAppend }: OrderByAddButtonProps) {
const queryFields = useQueryFields();
const { value, append, readonly } = useFieldArray<OrderByFieldSchema>();
const selectedFieldIDs = value?.map(({ fieldID }) => fieldID);
return (
<SelectFieldsButton
onSelect={id => {
append({ fieldID: id, isAsc: true });
afterAppend?.();
}}
selectedFieldIDs={selectedFieldIDs}
fields={queryFields}
filterSystemFields={false}
readonly={readonly}
testName="order-fileds-add-button"
/>
);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldArray, type FieldProps } from '@/form';
import { OrderBy } from './order-by';
export function OrderByField({ name }: Pick<FieldProps, 'name'>) {
return (
<FieldArray name={name}>
<OrderBy />
</FieldArray>
);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCurrentDatabaseField } from '@/node-registries/database/common/hooks';
import { DataTypeTag } from '@/node-registries/common/components';
import { Label, withField, useField } from '@/form';
import { type OrderByFieldSchema } from './types';
import { AceOrDescField } from './ace-or-desc-field';
export const OrderByItemField = withField(() => {
const { name, value } = useField<OrderByFieldSchema>();
const databaseField = useCurrentDatabaseField(value?.fieldID);
return (
<>
<Label
className="w-[138px]"
extra={<DataTypeTag type={databaseField?.type}></DataTypeTag>}
>
<span className="w-[90px] truncate">{databaseField?.name}</span>
</Label>
<AceOrDescField name={`${name}.isAsc`} type={databaseField?.type} />
</>
);
});

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
SortableList,
SortableItem,
FieldArrayList,
FieldArrayItem,
useFieldArray,
} from '@/form';
import { type OrderByFieldSchema } from './types';
import { OrderByItemField } from './order-by-item-field';
export function OrderByList() {
const { value, move, remove, name, readonly } =
useFieldArray<OrderByFieldSchema>();
return (
<FieldArrayList>
<SortableList
onSortEnd={({ from, to }) => {
move(from, to);
}}
>
{value?.map((item, index) => (
<SortableItem
key={item?.fieldID}
sortableID={item?.fieldID}
index={index}
>
<FieldArrayItem
disableRemove={readonly}
onRemove={() => {
remove(index);
}}
>
<OrderByItemField name={`${name}.${index}`} />
</FieldArrayItem>
</SortableItem>
))}
</SortableList>
</FieldArrayList>
);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type PropsWithChildren, useRef } from 'react';
import { I18n } from '@coze-arch/i18n';
import { Section, type SectionRefType, useFieldArray } from '@/form';
import { type OrderByFieldSchema } from './types';
import { OrderByAddButton } from './order-by-add-button';
export function OrderBySection({ children }: PropsWithChildren) {
const sectionRef = useRef<SectionRefType>();
const { value } = useFieldArray<OrderByFieldSchema>();
return (
<Section
ref={sectionRef}
title={I18n.t('workflow_order_by_title', {}, '排序字段')}
actions={[
<OrderByAddButton
afterAppend={() => {
sectionRef.current?.open();
}}
/>,
]}
isEmpty={!value || value.length === 0}
emptyText={I18n.t('workflow_order_by_empty', {}, '请添加排序字段')}
>
{children}
</Section>
);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useValidateOrderFields } from './use-validate-order-by-fields';
import { OrderBySection } from './order-by-section';
import { OrderByList } from './order-by-list';
export function OrderBy() {
useValidateOrderFields();
return (
<OrderBySection>
<OrderByList />
</OrderBySection>
);
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface OrderByFieldSchema {
fieldID: number;
isAsc: boolean;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useQueryFieldIDs } from './use-query-field-ids';
// 当前如果查询字段为空 则排序字段不显示
export function useOrderByVisible() {
const queryFieldIDs = useQueryFieldIDs();
return queryFieldIDs.length > 0;
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useWatch } from '@/form';
import { queryFieldsFieldName } from '@/constants/database-field-names';
import { type QueryFieldSchema } from '../query-fields-field/types';
export function useQueryFieldIDs() {
const queryFieldsFieldValue =
useWatch<QueryFieldSchema[]>(queryFieldsFieldName);
const selectedFieldIDs = queryFieldsFieldValue?.map(({ fieldID }) => fieldID);
return selectedFieldIDs;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useCurrentDatabaseQuery } from '@/hooks';
import { useQueryFieldIDs } from './use-query-field-ids';
export function useQueryFields() {
const queryFieldIDs = useQueryFieldIDs();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const queryFields = currentDatabase?.fields?.filter(item =>
queryFieldIDs?.includes(item.id),
);
return queryFields;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect } from 'react';
import { useFieldArray } from '@/form';
import { useQueryFieldIDs } from './use-query-field-ids';
import { type OrderByFieldSchema } from './types';
// 监听查询字段 当查询字段发生变化时 检查排序字段是否存在于查询字段中 不存在则移除
export const useValidateOrderFields = () => {
const { value, onChange } = useFieldArray<OrderByFieldSchema>();
const queryFieldIDs = useQueryFieldIDs();
useEffect(() => {
const fieldSchemaFiltered = value?.filter(({ fieldID }) =>
queryFieldIDs.includes(fieldID),
);
onChange(fieldSchemaFiltered || []);
}, [queryFieldIDs?.join(',')]);
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { QueryFieldsField } from './query-fields-field';

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanoid } from 'nanoid';
import { cloneDeep, set } from 'lodash-es';
import {
type ViewVariableMeta,
type ViewVariableType,
} from '@coze-workflow/base';
import { SelectFieldsButton } from '@/node-registries/database/common/components';
import { useCurrentDatabaseQuery } from '@/hooks';
import { useFieldArray, useForm } from '@/form';
import { type QueryFieldSchema } from './types';
interface QueryFieldsAddButtonProps {
afterAppend?: () => void;
}
export function QueryFieldsAddButton({
afterAppend,
}: QueryFieldsAddButtonProps) {
const { value, append, readonly } = useFieldArray<QueryFieldSchema>();
const selectedFieldIDs = value?.map(({ fieldID }) => fieldID);
const form = useForm();
const { data: currentDatabase } = useCurrentDatabaseQuery();
const outputs = form.getValueIn<ViewVariableMeta[]>('outputs');
return (
<SelectFieldsButton
onSelect={id => {
append({ fieldID: id, isDistinct: false });
const field = currentDatabase?.fields?.find(item => item.id === id);
const outputListField = cloneDeep(outputs)?.find(
item => item.name === 'outputList',
) as ViewVariableMeta;
const rowNumField = outputs?.find(item => item.name === 'rowNum');
const curIdField = outputListField?.children?.find(
item => item.name === field?.name,
);
if (!curIdField) {
if (!Array.isArray(outputListField?.children)) {
set(outputListField, 'children', []);
}
outputListField?.children?.push({
key: nanoid(),
name: field?.name ?? '',
type: field?.type as ViewVariableType,
});
form.setValueIn('outputs', [outputListField, rowNumField]);
}
afterAppend?.();
}}
selectedFieldIDs={selectedFieldIDs}
fields={currentDatabase?.fields}
filterSystemFields={false}
readonly={readonly}
/>
);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { ColumnTitles } from '@/form';
export function QueryFieldsColumnTitles() {
return (
<ColumnTitles
columns={[
{
label: I18n.t('workflow_query_fields_name', {}, '字段名'),
style: {
width: 249,
},
},
// {
// label: I18n.t('workflow_query_fields_remove_duplicates', {}, '去重'),
// },
]}
/>
);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldArray, type FieldProps } from '@/form';
import { QueryFields } from './query-fields';
export function QueryFieldsField({ name }: Pick<FieldProps, 'name'>) {
return (
<FieldArray name={name}>
<QueryFields />
</FieldArray>
);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useNodeTestId } from '@coze-workflow/base';
import { useCurrentDatabaseField } from '@/node-registries/database/common/hooks';
import { DataTypeTag } from '@/node-registries/common/components';
import { FieldArrayItem, Label, useFieldArray } from '@/form';
import { type QueryFieldSchema } from './types';
interface QueryFieldsItemProps {
index: number;
}
export function QueryFieldsItem({ index }: QueryFieldsItemProps) {
const { name, value, remove, readonly } = useFieldArray<QueryFieldSchema>();
const databaseField = useCurrentDatabaseField(value?.[index].fieldID);
const { getNodeSetterId } = useNodeTestId();
return (
<FieldArrayItem
disableRemove={readonly}
onRemove={() => remove(index)}
removeTestId={`${getNodeSetterId(name)}.remove`}
>
<Label
className="w-[249px]"
extra={<DataTypeTag type={databaseField?.type}></DataTypeTag>}
>
<span className="max-w-[200px] truncate">{databaseField?.name}</span>
</Label>
</FieldArrayItem>
);
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldArrayList, useFieldArray } from '@/form';
import { type QueryFieldSchema } from './types';
import { QueryFieldsItem } from './query-fields-item';
export function QueryFieldsList() {
const { value } = useFieldArray<QueryFieldSchema>();
return (
<FieldArrayList>
{value?.map((item, index) => <QueryFieldsItem index={index} />)}
</FieldArrayList>
);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type PropsWithChildren, useRef } from 'react';
import { I18n } from '@coze-arch/i18n';
import { Section, type SectionRefType, useFieldArray } from '@/form';
import { type QueryFieldSchema } from './types';
import { QueryFieldsAddButton } from './query-fields-add-button';
export function QueryFieldsSection({ children }: PropsWithChildren) {
const sectionRef = useRef<SectionRefType>();
const { value } = useFieldArray<QueryFieldSchema>();
return (
<Section
ref={sectionRef}
title={I18n.t('workflow_query_fields_title')}
isEmpty={!value || value.length === 0}
emptyText={I18n.t('workflow_query_fields_empty')}
actions={[
<QueryFieldsAddButton
afterAppend={() => {
sectionRef.current?.open();
}}
/>,
]}
>
{children}
</Section>
);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { QueryFieldsSection } from './query-fields-section';
import { QueryFieldsList } from './query-fields-list';
import { QueryFieldsColumnTitles } from './query-fields-column-titles';
export function QueryFields() {
return (
<QueryFieldsSection>
<QueryFieldsColumnTitles />
<QueryFieldsList />
</QueryFieldsSection>
);
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface QueryFieldSchema {
fieldID: number;
isDistinct?: boolean;
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { type FieldProps, InputNumberField, Section } from '@/form';
export function QueryLimitField({ name }: Pick<FieldProps, 'name'>) {
return (
<Section title={I18n.t('workflow_query_limit_title')}>
<InputNumberField className="w-full" name={name} defaultValue={100} />
</Section>
);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useResetCondition } from '@/node-registries/database/common/hooks';
import { useForm } from '@/form';
import {
queryFieldsFieldName,
orderByFieldName,
queryLimitFieldName,
queryConditionFieldName,
} from '@/constants/database-field-names';
export function useResetFields() {
const form = useForm();
const resetCondition = useResetCondition(queryConditionFieldName);
const resetQueryFields = () => {
form.setFieldValue(queryFieldsFieldName, undefined);
};
const resetOrderBy = () => {
form.setFieldValue(orderByFieldName, undefined);
};
const resetQueryLimit = () => {
form.setFieldValue(queryLimitFieldName, undefined);
};
return () => {
resetCondition();
resetQueryFields();
resetOrderBy();
resetQueryLimit();
};
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ValidateTrigger } from '@flowgram-adapter/free-layout-editor';
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { DatabaseNodeService } from '@/services/database-node-service';
import { provideNodeOutputVariablesEffect } from '@/nodes-v2/materials/provide-node-output-variables';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import {
createConditionValidator,
createDatabaseValidator,
createSelectAndSetFieldsValidator,
} from '@/node-registries/database/common/validators';
import { getOutputsDefaultValue } from '@/node-registries/database/common/utils';
import {
updateSelectAndSetFieldsFieldName,
updateConditionListFieldName,
updateConditionLogicFieldName,
} from '@/constants/database-field-names';
import { DatabaseUpdateForm } from './database-update-form';
export const DatabaseUpdateFormMeta: WorkflowNodeRegistry['formMeta'] = {
render: () => <DatabaseUpdateForm />,
validateTrigger: ValidateTrigger.onChange,
validate: {
nodeMeta: nodeMetaValidate,
...createDatabaseValidator(),
...createConditionValidator(updateConditionListFieldName),
...createSelectAndSetFieldsValidator(),
},
defaultValues: {
inputs: {
databaseInfoList: [],
},
outputs: getOutputsDefaultValue(),
},
formatOnInit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertSettingFieldDTOToField(
updateSelectAndSetFieldsFieldName,
value,
);
value = databaseNodeService.convertConditionDTOToCondition(
updateConditionListFieldName,
value,
);
value = databaseNodeService.convertConditionLogicDTOToConditionLogic(
updateConditionLogicFieldName,
value,
);
return value;
},
formatOnSubmit: (value, context) => {
const databaseNodeService =
context.node.getService<DatabaseNodeService>(DatabaseNodeService);
value = databaseNodeService.convertSettingFieldToDTO(
updateSelectAndSetFieldsFieldName,
value,
context.node,
);
value = databaseNodeService.convertConditionToDTO(
updateConditionListFieldName,
value,
context.node,
);
value = databaseNodeService.convertConditionLogicToConditionLogicDTO(
updateConditionLogicFieldName,
value,
);
return value;
},
effect: {
outputs: provideNodeOutputVariablesEffect,
},
};

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { useResetCondition } from '@/node-registries/database/common/hooks';
import {
ConditionField,
DatabaseSelectField,
OutputsField,
SelectAndSetFieldsField,
} from '@/node-registries/database/common/fields';
import { withNodeConfigForm } from '@/node-registries/common/hocs';
import { useCurrentDatabaseQuery } from '@/hooks';
import {
databaseSelectFieldName,
updateConditionFieldName,
updateSelectAndSetFieldsFieldName,
} from '@/constants/database-field-names';
import { useResetSelectAndSetFields } from './use-reset-select-and-set-fields';
export const DatabaseUpdateForm: React.FC = withNodeConfigForm(() => {
const { data: currentDatabase } = useCurrentDatabaseQuery();
const resetCondition = useResetCondition(updateConditionFieldName);
const resetSelectAndSetFields = useResetSelectAndSetFields();
return (
<>
<DatabaseSelectField
name={databaseSelectFieldName}
afterChange={() => {
resetCondition();
resetSelectAndSetFields();
}}
/>
{currentDatabase ? (
<ConditionField
label={I18n.t('workflow_update_condition_title')}
name={updateConditionFieldName}
min={1}
/>
) : null}
{currentDatabase ? (
<SelectAndSetFieldsField name={updateSelectAndSetFieldsFieldName} />
) : null}
<OutputsField name="outputs" />
</>
);
});

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type WorkflowNodeRegistry } from '@coze-workflow/nodes';
import { StandardNodeType } from '@coze-workflow/base';
import { type NodeTestMeta } from '@/test-run-kit';
import { test } from './node-test';
import { DatabaseUpdateFormMeta } from './database-update-form-meta';
export const DATABASE_UPDATE_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> =
{
meta: {
nodeDTOType: StandardNodeType.DatabaseUpdate,
test,
helpLink: '/open/docs/guides/database_update_node',
},
type: StandardNodeType.DatabaseUpdate,
formMeta: DatabaseUpdateFormMeta,
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DATABASE_UPDATE_NODE_REGISTRY } from './database-update-node-registry';

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MemoryApi } from '@coze-arch/bot-api';
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { generateParametersToProperties } from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
async generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const databaseID = formData?.inputs?.databaseInfoList[0]?.databaseInfoID;
if (!databaseID) {
return {};
}
const db = await MemoryApi.GetDatabaseByID({
id: databaseID,
need_sys_fields: true,
});
const fieldInfo = formData?.inputs?.updateParam?.fieldInfo ?? [];
const parameters = fieldInfo.map(item => {
const databaseField = db?.database_info?.field_list?.find(
field => field.alterId === item.fieldID,
);
return {
name: `__setting_field_${item?.fieldID}`,
title: databaseField?.name,
input: item?.fieldValue,
};
});
const conditionList = (
formData?.inputs?.updateParam?.condition?.conditionList ?? []
).map((item, idx) => {
const { left, right } = item;
const name = left;
const rightValue = right;
return {
name: `__condition_right_${idx}`,
title: `${name}`,
input: rightValue,
};
});
return {
...generateParametersToProperties(conditionList, { node }),
...generateParametersToProperties(parameters, { node }),
};
},
};

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useForm } from '@/form';
import { updateSelectAndSetFieldsFieldName } from '@/constants/database-field-names';
export function useResetSelectAndSetFields() {
const form = useForm();
return () => {
form.setFieldValue(updateSelectAndSetFieldsFieldName, undefined);
};
}