mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
chore(variable): move json schema logic from variable core to materials (#256)
This commit is contained in:
parent
f2abdd8cca
commit
11dda65150
@ -1,3 +1,4 @@
|
|||||||
|
import { JsonSchemaUtils } from '@flowgram.ai/form-materials';
|
||||||
import {
|
import {
|
||||||
definePluginCreator,
|
definePluginCreator,
|
||||||
FlowNodeVariableData,
|
FlowNodeVariableData,
|
||||||
@ -37,7 +38,7 @@ export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions>
|
|||||||
|
|
||||||
// Create an Type AST from the output data's JSON schema
|
// Create an Type AST from the output data's JSON schema
|
||||||
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
||||||
const typeAST = ASTFactory.createTypeASTFromSchema(value);
|
const typeAST = JsonSchemaUtils.schemaToAST(value);
|
||||||
|
|
||||||
if (typeAST) {
|
if (typeAST) {
|
||||||
// Use the node's title or its ID as the title for the variable
|
// Use the node's title or its ID as the title for the variable
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { VarJSONSchema } from '@flowgram.ai/fixed-layout-editor';
|
import type { IJsonSchema, IBasicJsonSchema } from '@flowgram.ai/form-materials';
|
||||||
|
|
||||||
export type BasicType = VarJSONSchema.BasicType;
|
export type BasicType = IBasicJsonSchema;
|
||||||
export type JsonSchema = VarJSONSchema.ISchema;
|
export type JsonSchema = IJsonSchema;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
FreeLayoutPluginContext,
|
FreeLayoutPluginContext,
|
||||||
ASTFactory,
|
ASTFactory,
|
||||||
} from '@flowgram.ai/free-layout-editor';
|
} from '@flowgram.ai/free-layout-editor';
|
||||||
|
import { JsonSchemaUtils } from '@flowgram.ai/form-materials';
|
||||||
|
|
||||||
export interface SyncVariablePluginOptions {}
|
export interface SyncVariablePluginOptions {}
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions>
|
|||||||
|
|
||||||
// Create an Type AST from the output data's JSON schema
|
// Create an Type AST from the output data's JSON schema
|
||||||
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
||||||
const typeAST = ASTFactory.createTypeASTFromSchema(value);
|
const typeAST = JsonSchemaUtils.schemaToAST(value);
|
||||||
|
|
||||||
if (typeAST) {
|
if (typeAST) {
|
||||||
// Use the node's title or its ID as the title for the variable
|
// Use the node's title or its ID as the title for the variable
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { VarJSONSchema } from '@flowgram.ai/free-layout-editor';
|
import type { IJsonSchema, IBasicJsonSchema } from '@flowgram.ai/form-materials';
|
||||||
|
|
||||||
export type BasicType = VarJSONSchema.BasicType;
|
export type BasicType = IBasicJsonSchema;
|
||||||
export type JsonSchema = VarJSONSchema.ISchema;
|
export type JsonSchema = IJsonSchema;
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PrivateScopeProvider, VarJSONSchema } from '@flowgram.ai/editor';
|
import { PrivateScopeProvider } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
import { VariableSelector, VariableSelectorProps } from '../variable-selector';
|
import { VariableSelector, VariableSelectorProps } from '../variable-selector';
|
||||||
|
import { IJsonSchema } from '../../typings';
|
||||||
|
|
||||||
const batchVariableSchema: VarJSONSchema.ISchema = {
|
const batchVariableSchema: IJsonSchema = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
extra: { weak: true },
|
extra: { weak: true },
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
import { IJsonSchema } from '../../typings';
|
||||||
|
|
||||||
export interface Strategy<Value = any> {
|
export interface Strategy<Value = any> {
|
||||||
hit: (schema: VarJSONSchema.ISchema) => boolean;
|
hit: (schema: IJsonSchema) => boolean;
|
||||||
Renderer: React.FC<RendererProps<Value>>;
|
Renderer: React.FC<RendererProps<Value>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ export interface RendererProps<Value = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PropsType extends RendererProps {
|
export interface PropsType extends RendererProps {
|
||||||
schema: VarJSONSchema.ISchema;
|
schema: IJsonSchema;
|
||||||
strategies?: Strategy[];
|
strategies?: Strategy[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
|
||||||
import { IconButton } from '@douyinfe/semi-ui';
|
import { IconButton } from '@douyinfe/semi-ui';
|
||||||
import { IconSetting } from '@douyinfe/semi-icons';
|
import { IconSetting } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
@ -9,6 +8,7 @@ import { ConstantInput } from '../constant-input';
|
|||||||
import { IFlowConstantRefValue } from '../../typings/flow-value';
|
import { IFlowConstantRefValue } from '../../typings/flow-value';
|
||||||
import { UIContainer, UIMain, UITrigger } from './styles';
|
import { UIContainer, UIMain, UITrigger } from './styles';
|
||||||
import { VariableSelector } from '../variable-selector';
|
import { VariableSelector } from '../variable-selector';
|
||||||
|
import { IJsonSchema } from '../../typings';
|
||||||
|
|
||||||
interface PropsType {
|
interface PropsType {
|
||||||
value?: IFlowConstantRefValue;
|
value?: IFlowConstantRefValue;
|
||||||
@ -16,7 +16,7 @@ interface PropsType {
|
|||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
schema?: VarJSONSchema.ISchema;
|
schema?: IJsonSchema;
|
||||||
constantProps?: {
|
constantProps?: {
|
||||||
strategies?: Strategy[];
|
strategies?: Strategy[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "json-schema-editor",
|
"name": "json-schema-editor",
|
||||||
"depMaterials": ["type-selector"],
|
"depMaterials": ["type-selector", "typings/json-schema"],
|
||||||
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons", "styled-components"]
|
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons", "styled-components"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { IJsonSchema } from '../../typings';
|
||||||
import { PropertyValueType } from './types';
|
import { PropertyValueType } from './types';
|
||||||
import { JsonSchema } from '../type-selector';
|
|
||||||
|
|
||||||
let _id = 0;
|
let _id = 0;
|
||||||
function genId() {
|
function genId() {
|
||||||
@ -100,7 +100,7 @@ export function usePropertiesEdit(
|
|||||||
const next = updater(_list);
|
const next = updater(_list);
|
||||||
|
|
||||||
// onChange to parent
|
// onChange to parent
|
||||||
const nextProperties: Record<string, JsonSchema> = {};
|
const nextProperties: Record<string, IJsonSchema> = {};
|
||||||
const nextRequired: string[] = [];
|
const nextRequired: string[] = [];
|
||||||
|
|
||||||
for (const _property of next) {
|
for (const _property of next) {
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import {
|
|||||||
IconMinus,
|
IconMinus,
|
||||||
} from '@douyinfe/semi-icons';
|
} from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
import { JsonSchema } from '../type-selector/types';
|
|
||||||
import { TypeSelector } from '../type-selector';
|
import { TypeSelector } from '../type-selector';
|
||||||
|
import { IJsonSchema } from '../../typings';
|
||||||
import { ConfigType, PropertyValueType } from './types';
|
import { ConfigType, PropertyValueType } from './types';
|
||||||
import {
|
import {
|
||||||
IconAddChildren,
|
IconAddChildren,
|
||||||
@ -33,8 +33,8 @@ import { UIRow } from './styles';
|
|||||||
import { usePropertiesEdit } from './hooks';
|
import { usePropertiesEdit } from './hooks';
|
||||||
|
|
||||||
export function JsonSchemaEditor(props: {
|
export function JsonSchemaEditor(props: {
|
||||||
value?: JsonSchema;
|
value?: IJsonSchema;
|
||||||
onChange?: (value: JsonSchema) => void;
|
onChange?: (value: IJsonSchema) => void;
|
||||||
config?: ConfigType;
|
config?: ConfigType;
|
||||||
}) {
|
}) {
|
||||||
const { value = { type: 'object' }, config = {}, onChange: onChangeProps } = props;
|
const { value = { type: 'object' }, config = {}, onChange: onChangeProps } = props;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { JsonSchema } from '../type-selector/types';
|
import { IJsonSchema } from '../../typings';
|
||||||
|
|
||||||
export interface PropertyValueType extends JsonSchema {
|
export interface PropertyValueType extends IJsonSchema {
|
||||||
name?: string;
|
name?: string;
|
||||||
key?: number;
|
key?: number;
|
||||||
isPropertyRequired?: boolean;
|
isPropertyRequired?: boolean;
|
||||||
@ -8,7 +8,7 @@ export interface PropertyValueType extends JsonSchema {
|
|||||||
|
|
||||||
export type PropertiesValueType = Pick<PropertyValueType, 'properties' | 'required'>;
|
export type PropertiesValueType = Pick<PropertyValueType, 'properties' | 'required'>;
|
||||||
|
|
||||||
export type JsonSchemaProperties = JsonSchema['properties'];
|
export type JsonSchemaProperties = IJsonSchema['properties'];
|
||||||
|
|
||||||
export interface ConfigType {
|
export interface ConfigType {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "type-selector",
|
"name": "type-selector",
|
||||||
"depMaterials": [],
|
"depMaterials": ["typings/json-schema"],
|
||||||
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons"]
|
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
import { CascaderData } from '@douyinfe/semi-ui/lib/es/cascader';
|
import { CascaderData } from '@douyinfe/semi-ui/lib/es/cascader';
|
||||||
import Icon from '@douyinfe/semi-icons';
|
import Icon from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
import { JsonSchema } from './types';
|
import { IJsonSchema } from '../../typings';
|
||||||
|
|
||||||
export const VariableTypeIcons: { [key: string]: React.ReactNode } = {
|
export const VariableTypeIcons: { [key: string]: React.ReactNode } = {
|
||||||
custom: (
|
custom: (
|
||||||
@ -271,7 +271,7 @@ export const ArrayIcons: { [key: string]: React.ReactNode } = {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSchemaIcon = (value?: Partial<JsonSchema>) => {
|
export const getSchemaIcon = (value?: Partial<IJsonSchema>) => {
|
||||||
if (value?.type === 'array') {
|
if (value?.type === 'array') {
|
||||||
return ArrayIcons[value.items?.type || 'object'];
|
return ArrayIcons[value.items?.type || 'object'];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,17 @@ import React, { useMemo } from 'react';
|
|||||||
|
|
||||||
import { Button, Cascader } from '@douyinfe/semi-ui';
|
import { Button, Cascader } from '@douyinfe/semi-ui';
|
||||||
|
|
||||||
import { JsonSchema } from './types';
|
import { IJsonSchema } from '../../typings';
|
||||||
import { ArrayIcons, VariableTypeIcons, getSchemaIcon, options } from './constants';
|
import { ArrayIcons, VariableTypeIcons, getSchemaIcon, options } from './constants';
|
||||||
|
|
||||||
interface PropTypes {
|
interface PropTypes {
|
||||||
value?: Partial<JsonSchema>;
|
value?: Partial<IJsonSchema>;
|
||||||
onChange: (value?: Partial<JsonSchema>) => void;
|
onChange: (value?: Partial<IJsonSchema>) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTypeSelectValue = (value?: Partial<JsonSchema>): string[] | undefined => {
|
export const getTypeSelectValue = (value?: Partial<IJsonSchema>): string[] | undefined => {
|
||||||
if (value?.type === 'array' && value?.items) {
|
if (value?.type === 'array' && value?.items) {
|
||||||
return [value.type, ...(getTypeSelectValue(value.items) || [])];
|
return [value.type, ...(getTypeSelectValue(value.items) || [])];
|
||||||
}
|
}
|
||||||
@ -20,7 +20,7 @@ export const getTypeSelectValue = (value?: Partial<JsonSchema>): string[] | unde
|
|||||||
return value?.type ? [value.type] : undefined;
|
return value?.type ? [value.type] : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseTypeSelectValue = (value?: string[]): Partial<JsonSchema> | undefined => {
|
export const parseTypeSelectValue = (value?: string[]): Partial<IJsonSchema> | undefined => {
|
||||||
const [type, ...subTypes] = value || [];
|
const [type, ...subTypes] = value || [];
|
||||||
|
|
||||||
if (type === 'array') {
|
if (type === 'array') {
|
||||||
@ -54,4 +54,4 @@ export function TypeSelector(props: PropTypes) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { JsonSchema, VariableTypeIcons, ArrayIcons, getSchemaIcon };
|
export { VariableTypeIcons, ArrayIcons, getSchemaIcon };
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
|
||||||
|
|
||||||
export type BasicType = VarJSONSchema.BasicType;
|
|
||||||
|
|
||||||
export type JsonSchema = VarJSONSchema.ISchema;
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "variable-selector",
|
"name": "variable-selector",
|
||||||
"depMaterials": ["type-selector"],
|
"depMaterials": ["type-selector", "utils/json-schema", "typings/json-schema"],
|
||||||
"depPackages": ["@douyinfe/semi-ui", "styled-components"]
|
"depPackages": ["@douyinfe/semi-ui", "styled-components"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
|
||||||
import { TriggerRenderProps } from '@douyinfe/semi-ui/lib/es/treeSelect';
|
import { TriggerRenderProps } from '@douyinfe/semi-ui/lib/es/treeSelect';
|
||||||
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
||||||
import { IconChevronDownStroked, IconIssueStroked } from '@douyinfe/semi-icons';
|
import { IconChevronDownStroked, IconIssueStroked } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
|
import { IJsonSchema } from '../../typings/json-schema';
|
||||||
import { useVariableTree } from './use-variable-tree';
|
import { useVariableTree } from './use-variable-tree';
|
||||||
import { UIRootTitle, UITag, UITreeSelect } from './styles';
|
import { UIRootTitle, UITag, UITreeSelect } from './styles';
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ interface PropTypes {
|
|||||||
notFoundContent?: string;
|
notFoundContent?: string;
|
||||||
};
|
};
|
||||||
onChange: (value?: string[]) => void;
|
onChange: (value?: string[]) => void;
|
||||||
includeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
includeSchema?: IJsonSchema | IJsonSchema[];
|
||||||
excludeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
excludeSchema?: IJsonSchema | IJsonSchema[];
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
import { useScopeAvailable, ASTMatch, BaseVariableField, VarJSONSchema } from '@flowgram.ai/editor';
|
import { useScopeAvailable, ASTMatch, BaseVariableField } from '@flowgram.ai/editor';
|
||||||
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
||||||
import { Icon } from '@douyinfe/semi-ui';
|
import { Icon } from '@douyinfe/semi-ui';
|
||||||
|
|
||||||
import { ArrayIcons, VariableTypeIcons } from '../type-selector/constants';
|
import { ArrayIcons, VariableTypeIcons } from '../type-selector/constants';
|
||||||
|
import { JsonSchemaUtils } from '../../utils/json-schema';
|
||||||
|
import { IJsonSchema } from '../../typings/json-schema';
|
||||||
|
|
||||||
type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
|
type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
|
||||||
|
|
||||||
export function useVariableTree(params: {
|
export function useVariableTree(params: {
|
||||||
includeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
includeSchema?: IJsonSchema | IJsonSchema[];
|
||||||
excludeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
excludeSchema?: IJsonSchema | IJsonSchema[];
|
||||||
}): TreeNodeData[] {
|
}): TreeNodeData[] {
|
||||||
const { includeSchema, excludeSchema } = params;
|
const { includeSchema, excludeSchema } = params;
|
||||||
|
|
||||||
@ -68,8 +70,12 @@ export function useVariableTree(params: {
|
|||||||
const keyPath = [...parentFields.map((_field) => _field.key), variable.key];
|
const keyPath = [...parentFields.map((_field) => _field.key), variable.key];
|
||||||
const key = keyPath.join('.');
|
const key = keyPath.join('.');
|
||||||
|
|
||||||
const isSchemaInclude = includeSchema ? type.isEqualWithJSONSchema(includeSchema) : true;
|
const isSchemaInclude = includeSchema
|
||||||
const isSchemaExclude = excludeSchema ? type.isEqualWithJSONSchema(excludeSchema) : false;
|
? JsonSchemaUtils.isASTMatchSchema(type, includeSchema)
|
||||||
|
: true;
|
||||||
|
const isSchemaExclude = excludeSchema
|
||||||
|
? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema)
|
||||||
|
: false;
|
||||||
const isSchemaMatch = isSchemaInclude && !isSchemaExclude;
|
const isSchemaMatch = isSchemaInclude && !isSchemaExclude;
|
||||||
|
|
||||||
// If not match, and no children, return null
|
// If not match, and no children, return null
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './flow-value';
|
export * from './flow-value';
|
||||||
|
export * from './json-schema';
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "json-schema",
|
||||||
|
"depMaterials": [],
|
||||||
|
"depPackages": []
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
export type JsonSchemaBasicType =
|
||||||
|
| 'boolean'
|
||||||
|
| 'string'
|
||||||
|
| 'integer'
|
||||||
|
| 'number'
|
||||||
|
| 'object'
|
||||||
|
| 'array'
|
||||||
|
| 'map';
|
||||||
|
|
||||||
|
export interface IJsonSchema<T = string> {
|
||||||
|
type?: T;
|
||||||
|
default?: any;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
enum?: (string | number)[];
|
||||||
|
properties?: Record<string, IJsonSchema<T>>;
|
||||||
|
additionalProperties?: IJsonSchema<T>;
|
||||||
|
items?: IJsonSchema<T>;
|
||||||
|
required?: string[];
|
||||||
|
$ref?: string;
|
||||||
|
extra?: {
|
||||||
|
index?: number;
|
||||||
|
// Used in BaseType.isEqualWithJSONSchema, the type comparison will be weak
|
||||||
|
weak?: boolean;
|
||||||
|
// Set the render component
|
||||||
|
formComponent?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IBasicJsonSchema = IJsonSchema<JsonSchemaBasicType>;
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from './format-legacy-refs';
|
export * from './format-legacy-refs';
|
||||||
|
export * from './json-schema';
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "json-schema",
|
||||||
|
"depMaterials": ["typings/json-schema"],
|
||||||
|
"depPackages": ["lodash"]
|
||||||
|
}
|
||||||
154
packages/materials/form-materials/src/utils/json-schema/index.ts
Normal file
154
packages/materials/form-materials/src/utils/json-schema/index.ts
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import { get } from 'lodash';
|
||||||
|
import { ASTFactory, ASTKind, ASTMatch, ASTNode, ASTNodeJSON, BaseType } from '@flowgram.ai/editor';
|
||||||
|
|
||||||
|
import { IJsonSchema } from '../../typings/json-schema';
|
||||||
|
|
||||||
|
export namespace JsonSchemaUtils {
|
||||||
|
/**
|
||||||
|
* Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
|
||||||
|
* This function recursively processes the JSON schema and creates corresponding AST nodes.
|
||||||
|
*
|
||||||
|
* For more information on JSON Schema, refer to the official documentation:
|
||||||
|
* https://json-schema.org/
|
||||||
|
*
|
||||||
|
* @param jsonSchema - The JSON schema to convert.
|
||||||
|
* @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
|
||||||
|
*/
|
||||||
|
export function schemaToAST(jsonSchema: IJsonSchema): ASTNodeJSON | undefined {
|
||||||
|
const { type, extra } = jsonSchema || {};
|
||||||
|
const { weak = false } = extra || {};
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'object':
|
||||||
|
if (weak) {
|
||||||
|
return { kind: ASTKind.Object, weak: true };
|
||||||
|
}
|
||||||
|
return ASTFactory.createObject({
|
||||||
|
properties: Object.entries(jsonSchema.properties || {})
|
||||||
|
/**
|
||||||
|
* Sorts the properties of a JSON schema based on the 'extra.index' field.
|
||||||
|
* If the 'extra.index' field is not present, the property will be treated as having an index of 0.
|
||||||
|
*/
|
||||||
|
.sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
|
||||||
|
.map(([key, _property]) => ({
|
||||||
|
key,
|
||||||
|
type: schemaToAST(_property),
|
||||||
|
meta: { description: _property.description },
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
case 'array':
|
||||||
|
if (weak) {
|
||||||
|
return { kind: ASTKind.Array, weak: true };
|
||||||
|
}
|
||||||
|
return ASTFactory.createArray({
|
||||||
|
items: schemaToAST(jsonSchema.items!),
|
||||||
|
});
|
||||||
|
case 'map':
|
||||||
|
if (weak) {
|
||||||
|
return { kind: ASTKind.Map, weak: true };
|
||||||
|
}
|
||||||
|
return ASTFactory.createMap({
|
||||||
|
valueType: schemaToAST(jsonSchema.additionalProperties!),
|
||||||
|
});
|
||||||
|
case 'string':
|
||||||
|
return ASTFactory.createString();
|
||||||
|
case 'number':
|
||||||
|
return ASTFactory.createNumber();
|
||||||
|
case 'boolean':
|
||||||
|
return ASTFactory.createBoolean();
|
||||||
|
case 'integer':
|
||||||
|
return ASTFactory.createInteger();
|
||||||
|
|
||||||
|
default:
|
||||||
|
// If the type is not recognized, return CustomType
|
||||||
|
return ASTFactory.createCustomType({ typeName: type });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert AST To JSON Schema
|
||||||
|
* @param typeAST
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function astToSchema(typeAST: ASTNode): IJsonSchema | undefined {
|
||||||
|
if (ASTMatch.isString(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'string',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isBoolean(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'boolean',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isNumber(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'number',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isInteger(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'integer',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isObject(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: Object.fromEntries(
|
||||||
|
Object.entries(typeAST.properties).map(([key, value]) => [key, astToSchema(value)!])
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isArray(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'array',
|
||||||
|
items: astToSchema(typeAST.items),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isMap(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: 'map',
|
||||||
|
items: astToSchema(typeAST.valueType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASTMatch.isCustomType(typeAST)) {
|
||||||
|
return {
|
||||||
|
type: typeAST.typeName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the AST type is match the JSON Schema
|
||||||
|
* @param typeAST
|
||||||
|
* @param schema
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isASTMatchSchema(
|
||||||
|
typeAST: BaseType,
|
||||||
|
schema: IJsonSchema | IJsonSchema[]
|
||||||
|
): boolean {
|
||||||
|
if (Array.isArray(schema)) {
|
||||||
|
return typeAST.isTypeEqual(
|
||||||
|
ASTFactory.createUnion({
|
||||||
|
types: schema.map((_schema) => schemaToAST(_schema)!).filter(Boolean),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeAST.isTypeEqual(schemaToAST(schema));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,5 @@
|
|||||||
import { get } from 'lodash';
|
|
||||||
|
|
||||||
import { ASTKind, ASTNodeJSON } from './types';
|
import { ASTKind, ASTNodeJSON } from './types';
|
||||||
import { MapJSON } from './type/map';
|
import { MapJSON } from './type/map';
|
||||||
import { VarJSONSchema } from './type/json-schema';
|
|
||||||
import { ArrayJSON } from './type/array';
|
import { ArrayJSON } from './type/array';
|
||||||
import { CustomTypeJSON, ObjectJSON, UnionJSON } from './type';
|
import { CustomTypeJSON, ObjectJSON, UnionJSON } from './type';
|
||||||
import {
|
import {
|
||||||
@ -77,74 +74,6 @@ export namespace ASTFactory {
|
|||||||
...json,
|
...json,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
|
|
||||||
* This function recursively processes the JSON schema and creates corresponding AST nodes.
|
|
||||||
*
|
|
||||||
* For more information on JSON Schema, refer to the official documentation:
|
|
||||||
* https://json-schema.org/
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param jsonSchema - The JSON schema to convert.
|
|
||||||
* @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
|
|
||||||
*/
|
|
||||||
export function createTypeASTFromSchema(
|
|
||||||
jsonSchema: VarJSONSchema.ISchema
|
|
||||||
): ASTNodeJSON | undefined {
|
|
||||||
const { type, extra } = jsonSchema || {};
|
|
||||||
const { weak = false } = extra || {};
|
|
||||||
|
|
||||||
if (!type) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'object':
|
|
||||||
if (weak) {
|
|
||||||
return { kind: ASTKind.Object, weak: true };
|
|
||||||
}
|
|
||||||
return ASTFactory.createObject({
|
|
||||||
properties: Object.entries(jsonSchema.properties || {})
|
|
||||||
/**
|
|
||||||
* Sorts the properties of a JSON schema based on the 'extra.index' field.
|
|
||||||
* If the 'extra.index' field is not present, the property will be treated as having an index of 0.
|
|
||||||
*/
|
|
||||||
.sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
|
|
||||||
.map(([key, _property]) => ({
|
|
||||||
key,
|
|
||||||
type: createTypeASTFromSchema(_property),
|
|
||||||
meta: { description: _property.description },
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
case 'array':
|
|
||||||
if (weak) {
|
|
||||||
return { kind: ASTKind.Array, weak: true };
|
|
||||||
}
|
|
||||||
return ASTFactory.createArray({
|
|
||||||
items: createTypeASTFromSchema(jsonSchema.items!),
|
|
||||||
});
|
|
||||||
case 'map':
|
|
||||||
if (weak) {
|
|
||||||
return { kind: ASTKind.Map, weak: true };
|
|
||||||
}
|
|
||||||
return ASTFactory.createMap({
|
|
||||||
valueType: createTypeASTFromSchema(jsonSchema.additionalProperties!),
|
|
||||||
});
|
|
||||||
case 'string':
|
|
||||||
return ASTFactory.createString();
|
|
||||||
case 'number':
|
|
||||||
return ASTFactory.createNumber();
|
|
||||||
case 'boolean':
|
|
||||||
return ASTFactory.createBoolean();
|
|
||||||
case 'integer':
|
|
||||||
return ASTFactory.createInteger();
|
|
||||||
|
|
||||||
default:
|
|
||||||
// If the type is not recognized, return CustomType
|
|
||||||
return ASTFactory.createCustomType({ typeName: type });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过 AST Class 创建
|
* 通过 AST Class 创建
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
||||||
import { ASTNodeFlags } from '../flags';
|
import { ASTNodeFlags } from '../flags';
|
||||||
import { BaseVariableField } from '../declaration';
|
import { BaseVariableField } from '../declaration';
|
||||||
import { ASTNode } from '../ast-node';
|
import { ASTNode } from '../ast-node';
|
||||||
import { UnionJSON } from './union';
|
import { UnionJSON } from './union';
|
||||||
import { ASTFactory } from '../factory';
|
|
||||||
|
|
||||||
export abstract class BaseType<JSON extends ASTNodeJSON = any, InjectOpts = any> extends ASTNode<
|
export abstract class BaseType<JSON extends ASTNodeJSON = any, InjectOpts = any> extends ASTNode<
|
||||||
JSON,
|
JSON,
|
||||||
@ -47,31 +45,4 @@ export abstract class BaseType<JSON extends ASTNodeJSON = any, InjectOpts = any>
|
|||||||
kind: this.kind,
|
kind: this.kind,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Standard JSON Schema for current base type
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: this.kind.toLowerCase() as VarJSONSchema.BasicType,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the type is equal with json schema
|
|
||||||
*/
|
|
||||||
isEqualWithJSONSchema(schema: VarJSONSchema.ISchema | VarJSONSchema.ISchema[]): boolean {
|
|
||||||
if (Array.isArray(schema)) {
|
|
||||||
return this.isTypeEqual(
|
|
||||||
ASTFactory.createUnion({
|
|
||||||
types: schema
|
|
||||||
.map((_schema) => ASTFactory.createTypeASTFromSchema(_schema)!)
|
|
||||||
.filter(Boolean),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.isTypeEqual(ASTFactory.createTypeASTFromSchema(schema));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind } from '../types';
|
import { ASTKind } from '../types';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
|
|
||||||
@ -8,10 +7,4 @@ export class BooleanType extends BaseType {
|
|||||||
fromJSON(): void {
|
fromJSON(): void {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'boolean',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind, ASTNodeJSONOrKind } from '../types';
|
import { ASTKind, ASTNodeJSONOrKind } from '../types';
|
||||||
import { type UnionJSON } from './union';
|
import { type UnionJSON } from './union';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
@ -36,10 +35,4 @@ export class CustomType extends BaseType<CustomTypeJSON> {
|
|||||||
|
|
||||||
return targetTypeJSON?.kind === this.kind && targetTypeJSON?.typeName === this.typeName;
|
return targetTypeJSON?.kind === this.kind && targetTypeJSON?.typeName === this.typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: this._typeName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,4 +12,3 @@ export {
|
|||||||
export { BaseType } from './base-type';
|
export { BaseType } from './base-type';
|
||||||
export { type UnionJSON } from './union';
|
export { type UnionJSON } from './union';
|
||||||
export { CustomType, type CustomTypeJSON } from './custom-type';
|
export { CustomType, type CustomTypeJSON } from './custom-type';
|
||||||
export { VarJSONSchema } from './json-schema';
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind } from '../types';
|
import { ASTKind } from '../types';
|
||||||
import { ASTNodeFlags } from '../flags';
|
import { ASTNodeFlags } from '../flags';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
@ -11,10 +10,4 @@ export class IntegerType extends BaseType {
|
|||||||
fromJSON(): void {
|
fromJSON(): void {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'integer',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
export namespace VarJSONSchema {
|
|
||||||
export type BasicType = 'boolean' | 'string' | 'integer' | 'number' | 'object' | 'array' | 'map';
|
|
||||||
|
|
||||||
export interface ISchema<T = string> {
|
|
||||||
type?: T;
|
|
||||||
default?: any;
|
|
||||||
title?: string;
|
|
||||||
description?: string;
|
|
||||||
enum?: (string | number)[];
|
|
||||||
properties?: Record<string, ISchema<T>>;
|
|
||||||
additionalProperties?: ISchema<T>;
|
|
||||||
items?: ISchema<T>;
|
|
||||||
required?: string[];
|
|
||||||
$ref?: string;
|
|
||||||
extra?: {
|
|
||||||
index?: number;
|
|
||||||
// Used in BaseType.isEqualWithJSONSchema, the type comparison will be weak
|
|
||||||
weak?: boolean;
|
|
||||||
// Set the render component
|
|
||||||
formComponent?: string;
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IBasicSchema = ISchema<BasicType>;
|
|
||||||
}
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
|
|
||||||
@ -75,11 +74,4 @@ export class MapType extends BaseType<MapJSON> {
|
|||||||
valueType: this.valueType?.toJSON(),
|
valueType: this.valueType?.toJSON(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'map',
|
|
||||||
additionalProperties: this.valueType?.toJSONSchema(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind } from '../types';
|
import { ASTKind } from '../types';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
|
|
||||||
@ -8,10 +7,4 @@ export class NumberType extends BaseType {
|
|||||||
fromJSON(): void {
|
fromJSON(): void {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'number',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { xor } from 'lodash';
|
import { xor } from 'lodash';
|
||||||
|
|
||||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTNodeJSON, ASTKind, ASTNodeJSONOrKind, type GlobalEventActionType } from '../types';
|
import { ASTNodeJSON, ASTKind, ASTNodeJSONOrKind, type GlobalEventActionType } from '../types';
|
||||||
import { ASTNodeFlags } from '../flags';
|
import { ASTNodeFlags } from '../flags';
|
||||||
import { Property, type PropertyJSON } from '../declaration/property';
|
import { Property, type PropertyJSON } from '../declaration/property';
|
||||||
@ -149,14 +148,4 @@ export class ObjectType extends BaseType<ObjectJSON> {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'object',
|
|
||||||
properties: this.properties.reduce((acc, _property) => {
|
|
||||||
acc[_property.key] = _property.type?.toJSONSchema();
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, VarJSONSchema.ISchema>),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { VarJSONSchema } from './json-schema';
|
|
||||||
import { ASTKind } from '../types';
|
import { ASTKind } from '../types';
|
||||||
import { ASTNodeFlags } from '../flags';
|
import { ASTNodeFlags } from '../flags';
|
||||||
import { BaseType } from './base-type';
|
import { BaseType } from './base-type';
|
||||||
@ -11,10 +10,4 @@ export class StringType extends BaseType {
|
|||||||
fromJSON(): void {
|
fromJSON(): void {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSONSchema(): VarJSONSchema.ISchema {
|
|
||||||
return {
|
|
||||||
type: 'string',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user