mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-17 10:05:59 +08:00
feat: implement JSON schema depth validation and update related components
This commit is contained in:
parent
ffe08a35a4
commit
7a647cf18e
@ -6,6 +6,8 @@ import { RiClipboardLine, RiCloseLine, RiErrorWarningFill, RiIndentIncrease } fr
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { Editor } from '@monaco-editor/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { checkDepth } from '../../utils'
|
||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||
|
||||
type JsonImporterProps = {
|
||||
onSubmit: (schema: string) => void
|
||||
@ -71,8 +73,17 @@ const JsonImporter: FC<JsonImporterProps> = ({
|
||||
const handleSubmit = useCallback(() => {
|
||||
try {
|
||||
const parsedJSON = JSON.parse(json)
|
||||
const maxDepth = checkDepth(parsedJSON)
|
||||
if (maxDepth > JSON_SCHEMA_MAX_DEPTH) {
|
||||
setParseError({
|
||||
type: 'error',
|
||||
message: `Schema exceeds maximum depth of ${JSON_SCHEMA_MAX_DEPTH}.`,
|
||||
})
|
||||
return
|
||||
}
|
||||
onSubmit(parsedJSON)
|
||||
setParseError(null)
|
||||
setOpen(false)
|
||||
}
|
||||
catch (e: any) {
|
||||
if (e instanceof SyntaxError)
|
||||
|
@ -281,6 +281,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
})
|
||||
|
||||
useSubscribe('fieldChange', (params) => {
|
||||
let samePropertyNameError = false
|
||||
const { parentPath, oldFields, fields } = params as ChangeEventParams
|
||||
const newSchema = produce(jsonSchema, (draft) => {
|
||||
const parentSchema = findPropertyWithPath(draft, parentPath) as Field
|
||||
@ -295,7 +296,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
type: 'error',
|
||||
message: 'Property name already exists',
|
||||
})
|
||||
return
|
||||
samePropertyNameError = true
|
||||
}
|
||||
|
||||
const newProperties = Object.entries(properties).reduce((acc, [key, value]) => {
|
||||
@ -384,8 +385,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
type: 'error',
|
||||
message: 'Property name already exists',
|
||||
})
|
||||
emit('restorePropertyName')
|
||||
return
|
||||
samePropertyNameError = true
|
||||
}
|
||||
|
||||
const newProperties = Object.entries(properties).reduce((acc, [key, value]) => {
|
||||
@ -463,6 +463,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
schema.enum = fields.enum
|
||||
}
|
||||
})
|
||||
if (samePropertyNameError) return
|
||||
setJsonSchema(newSchema)
|
||||
emit('fieldChangeSuccess')
|
||||
})
|
||||
|
@ -14,6 +14,7 @@ import { useJsonSchemaConfigStore } from '../../store'
|
||||
import { useMittContext } from '../../context'
|
||||
import produce from 'immer'
|
||||
import { useUnmount } from 'ahooks'
|
||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||
|
||||
export type EditData = {
|
||||
name: string
|
||||
@ -46,8 +47,6 @@ const TYPE_OPTIONS = [
|
||||
{ value: ArrayType.object, text: 'array[object]' },
|
||||
]
|
||||
|
||||
const DEPTH_LIMIT = 10
|
||||
|
||||
const EditCard: FC<EditCardProps> = ({
|
||||
fields,
|
||||
depth,
|
||||
@ -64,7 +63,7 @@ const EditCard: FC<EditCardProps> = ({
|
||||
const { emit, useSubscribe } = useMittContext()
|
||||
const blurWithActions = useRef(false)
|
||||
|
||||
const disableAddBtn = fields.type !== Type.object && fields.type !== ArrayType.object && depth < DEPTH_LIMIT
|
||||
const disableAddBtn = depth >= JSON_SCHEMA_MAX_DEPTH || (fields.type !== Type.object && fields.type !== ArrayType.object)
|
||||
const hasAdvancedOptions = fields.type === Type.string || fields.type === Type.number
|
||||
const isAdvancedEditing = advancedEditing || isAddingNewField
|
||||
|
||||
|
@ -16,7 +16,7 @@ const VisualEditor: FC<VisualEditorProps> = ({
|
||||
schema={schema}
|
||||
required={false}
|
||||
path={[]}
|
||||
depth={0}
|
||||
depth={1}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -10,6 +10,7 @@ import Card from './card'
|
||||
import { useJsonSchemaConfigStore } from '../store'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import AddField from './add-field'
|
||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||
|
||||
type SchemaNodeProps = {
|
||||
name: string
|
||||
@ -22,28 +23,28 @@ type SchemaNodeProps = {
|
||||
|
||||
// Support 10 levels of indentation
|
||||
const indentPadding: Record<number, string> = {
|
||||
0: 'pl-0',
|
||||
1: 'pl-[20px]',
|
||||
2: 'pl-[40px]',
|
||||
3: 'pl-[60px]',
|
||||
4: 'pl-[80px]',
|
||||
5: 'pl-[100px]',
|
||||
6: 'pl-[120px]',
|
||||
7: 'pl-[140px]',
|
||||
8: 'pl-[160px]',
|
||||
9: 'pl-[180px]',
|
||||
1: 'pl-0',
|
||||
2: 'pl-[20px]',
|
||||
3: 'pl-[40px]',
|
||||
4: 'pl-[60px]',
|
||||
5: 'pl-[80px]',
|
||||
6: 'pl-[100px]',
|
||||
7: 'pl-[120px]',
|
||||
8: 'pl-[140px]',
|
||||
9: 'pl-[160px]',
|
||||
10: 'pl-[180px]',
|
||||
}
|
||||
|
||||
const indentLeft: Record<number, string> = {
|
||||
1: 'left-0',
|
||||
2: 'left-[20px]',
|
||||
3: 'left-[40px]',
|
||||
4: 'left-[60px]',
|
||||
5: 'left-[80px]',
|
||||
6: 'left-[100px]',
|
||||
7: 'left-[120px]',
|
||||
8: 'left-[140px]',
|
||||
9: 'left-[160px]',
|
||||
2: 'left-0',
|
||||
3: 'left-[20px]',
|
||||
4: 'left-[40px]',
|
||||
5: 'left-[60px]',
|
||||
6: 'left-[80px]',
|
||||
7: 'left-[100px]',
|
||||
8: 'left-[120px]',
|
||||
9: 'left-[140px]',
|
||||
10: 'left-[160px]',
|
||||
}
|
||||
|
||||
const SchemaNode: FC<SchemaNodeProps> = ({
|
||||
@ -66,7 +67,7 @@ const SchemaNode: FC<SchemaNodeProps> = ({
|
||||
|
||||
const hasChildren = getHasChildren(schema)
|
||||
const type = getFieldType(schema)
|
||||
const isHovering = hoveringProperty === path.join('.') && depth > 0
|
||||
const isHovering = hoveringProperty === path.join('.') && depth > 1
|
||||
|
||||
const handleExpand = () => {
|
||||
setIsExpanded(!isExpanded)
|
||||
@ -85,7 +86,7 @@ const SchemaNode: FC<SchemaNodeProps> = ({
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className={classNames('relative z-10', indentPadding[depth])}>
|
||||
{depth > 0 && hasChildren && (
|
||||
{depth > 1 && hasChildren && (
|
||||
<div className={classNames(
|
||||
'flex items-center absolute top-0 w-5 h-7 px-0.5 z-10 bg-background-section-burn',
|
||||
indentLeft[depth],
|
||||
@ -139,7 +140,7 @@ const SchemaNode: FC<SchemaNodeProps> = ({
|
||||
<Divider type='vertical' className='bg-divider-subtle mx-0' />
|
||||
</div>
|
||||
|
||||
{isExpanded && hasChildren && (
|
||||
{isExpanded && hasChildren && depth < JSON_SCHEMA_MAX_DEPTH && (
|
||||
<>
|
||||
{schema.type === Type.object && schema.properties && (
|
||||
Object.entries(schema.properties).map(([key, childSchema]) => (
|
||||
@ -176,7 +177,7 @@ const SchemaNode: FC<SchemaNodeProps> = ({
|
||||
)}
|
||||
|
||||
{
|
||||
depth === 0 && !isAddingNewField && (
|
||||
depth === 1 && !isAddingNewField && (
|
||||
<AddField />
|
||||
)
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ export const inferType = (value: any): Type => {
|
||||
return Type.string
|
||||
}
|
||||
|
||||
export function jsonToSchema(json: any): Field {
|
||||
export const jsonToSchema = (json: any): Field => {
|
||||
const schema: Field = {
|
||||
type: inferType(json),
|
||||
}
|
||||
|
||||
if (schema.type === 'object') {
|
||||
if (schema.type === Type.object) {
|
||||
schema.properties = {}
|
||||
schema.required = []
|
||||
schema.additionalProperties = false
|
||||
@ -53,3 +53,22 @@ export function jsonToSchema(json: any): Field {
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
export const checkDepth = (json: any, currentDepth = 1) => {
|
||||
const type = inferType(json)
|
||||
if (type !== Type.object && type !== Type.array)
|
||||
return currentDepth
|
||||
|
||||
let maxDepth = currentDepth
|
||||
if (type === Type.object) {
|
||||
Object.keys(json).forEach((key) => {
|
||||
const depth = checkDepth(json[key], currentDepth + 1)
|
||||
maxDepth = Math.max(maxDepth, depth)
|
||||
})
|
||||
}
|
||||
else if (type === Type.array && json.length > 0) {
|
||||
const depth = checkDepth(json[0], currentDepth + 1)
|
||||
maxDepth = Math.max(maxDepth, depth)
|
||||
}
|
||||
return maxDepth
|
||||
}
|
||||
|
@ -276,3 +276,5 @@ export const GITHUB_ACCESS_TOKEN = process.env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN |
|
||||
|
||||
export const SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS = '.difypkg,.difybndl'
|
||||
export const FULL_DOC_PREVIEW_LENGTH = 50
|
||||
|
||||
export const JSON_SCHEMA_MAX_DEPTH = 10
|
||||
|
Loading…
x
Reference in New Issue
Block a user