diff --git a/webui/src/components/InferenceForm.tsx b/webui/src/components/InferenceForm.tsx index 0800a03d8c8..ad0048542f8 100644 --- a/webui/src/components/InferenceForm.tsx +++ b/webui/src/components/InferenceForm.tsx @@ -1,5 +1,5 @@ -import type { Key } from 'react' -import { useMemo, useState } from 'react' +import type { FC, Key } from 'react' +import { createElement, useMemo, useState } from 'react' import { useStyletron } from 'baseui' import { Select } from 'baseui/select' import { FormControl } from 'baseui/form-control' @@ -8,13 +8,19 @@ import { FormConsumer } from '@formily/react' import { FlexGrid, FlexGridItem } from 'baseui/flex-grid' import { Tab, Tabs } from 'baseui/tabs' import useCurrentPath from '../hooks/useCurrentPath' -import { useFormSubmit, useSchema } from '../hooks/useQuery' +import { transformData, useFormSubmit, useSchema } from '../hooks/useQuery' import Form from './form/Form' import FormField, { generateFormSchema } from './form/FormField' import Submit from './form/Submit' +import type { IClientProps } from './code/Base' import CURL from './code/CURL' import Python from './code/Python' +const codeExamples = { + Python, + CURL, +} + export default function InferenceForm() { const [css, theme] = useStyletron() const data = useSchema() @@ -96,16 +102,19 @@ export default function InferenceForm() { Submit - - - {() => } - - - - - {() => } - - + { + Object.entries(codeExamples).map(([title, comp]) => ( + + + {() => createElement(comp as FC, { + path: currentRoute?.route, + values: transformData(form.values, currentRoute?.input), + schema: currentRoute?.input, + })} + + + )) + } diff --git a/webui/src/components/code/CURL.tsx b/webui/src/components/code/CURL.tsx index ef12caef9a6..15d5e3f654c 100644 --- a/webui/src/components/code/CURL.tsx +++ b/webui/src/components/code/CURL.tsx @@ -1,5 +1,5 @@ import isEmpty from 'lodash/isEmpty' -import { convert, hasFileInSchema } from '../../hooks/useQuery' +import { hasFileInSchema, splitFileAndNonFileFields } from '../../hooks/useQuery' import type { TObject } from '../../types' import type { IClientProps } from './Base' import Code, { formatJSON } from './Base' @@ -28,7 +28,7 @@ function formatFiles(files: { [field: string]: File | File[] }, indent = 4) { function generateCode(data: object, path = '/', schema?: TObject) { const hasFiles = hasFileInSchema(schema ? { schema } : {}) if (hasFiles) { - const { nonFileFields, fileFields } = convert(data) + const { nonFileFields, fileFields } = splitFileAndNonFileFields(data) return `$ curl -s -X POST \\ -F '__data=${formatJSON(nonFileFields, 4)}' \\ diff --git a/webui/src/hooks/useQuery.ts b/webui/src/hooks/useQuery.ts index ab111f1e9e3..a9daa72d811 100644 --- a/webui/src/hooks/useQuery.ts +++ b/webui/src/hooks/useQuery.ts @@ -2,6 +2,7 @@ import { useCallback, useContext } from 'react' import type { Form } from '@formily/core' import isObject from 'lodash/isObject' import findKey from 'lodash/findKey' +import transform from 'lodash/transform' import { JSONSchemaContext } from '../components/JSONSchema' import type { DataType, TObject } from '../types' @@ -45,12 +46,45 @@ export function isFileField(value: DataType): boolean { } /** - * Converts an object by separating File objects and other data. - * @param srcValue The source object to convert. + * Transforms the source value according to the provided schema. + * @param {any} srcValue - The value to be transformed. + * @param {DataType} [schema] - The schema defining the transformation rules. + * @returns {any} - The transformed data. + */ +export function transformData(srcValue: any, schema?: DataType) { + if (!schema) + return srcValue + switch (schema?.type) { + case 'object': + if (schema.properties) { + return transform(schema.properties, (res, value, key) => { + res[key] = transformData(srcValue[key], value) + return res + }, {}) + } + else { + try { + return JSON.parse(srcValue) + } + catch { + // return empty object when JSON string parse failed + return {} + } + } + case 'array': + return srcValue?.map((item: any) => transformData(item, schema.items)) + default: + return srcValue + } +} + +/** + * Splits the source object into two separate objects based on field types: non-file fields and file fields. + * @param srcValue The source object containing the fields to be split. This object can be nested. * @param parentPath The path used to construct the key for nested objects. * @returns An object containing separated nonFileFields and fileFields. */ -export function convert(srcValue: object, parentPath = '') { +export function splitFileAndNonFileFields(srcValue: object, parentPath = '') { const nonFileFields: { [key: string]: unknown } = {} const fileFields: { [key: string]: File | File[] } = {} @@ -62,7 +96,7 @@ export function convert(srcValue: object, parentPath = '') { fileFields[path] = value } else if (isObject(value)) { - const res = convert(value, path) + const res = splitFileAndNonFileFields(value, path) nonFileFields[key] = res.nonFileFields Object.assign(fileFields, res.fileFields) } @@ -77,10 +111,11 @@ export function convert(srcValue: object, parentPath = '') { export function useFormSubmit(form: Form, input?: TObject) { return useCallback(async (url: string) => { const submittedFormData = await form.submit() + const transformedData = transformData(submittedFormData, input) const hasFiles = hasFileInSchema(input ? { input } : {}) if (hasFiles) { - const { nonFileFields, fileFields } = convert(submittedFormData) + const { nonFileFields, fileFields } = splitFileAndNonFileFields(transformedData) const formData = new FormData() formData.append('__data', JSON.stringify(nonFileFields))