Skip to content

Commit

Permalink
fix: value is JSON string when datatype is object and properties is e…
Browse files Browse the repository at this point in the history
…mpty
  • Loading branch information
jinyang1994 committed Dec 4, 2023
1 parent 4d9f364 commit a619f51
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
35 changes: 22 additions & 13 deletions webui/src/components/InferenceForm.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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()
Expand Down Expand Up @@ -96,16 +102,19 @@ export default function InferenceForm() {
<FormField schema={formSchema} />
<Submit>Submit</Submit>
</Tab>
<Tab title="Python">
<FormConsumer>
{() => <Python path={currentRoute?.route} values={form.values} schema={currentRoute?.input} />}
</FormConsumer>
</Tab>
<Tab title="CURL">
<FormConsumer>
{() => <CURL path={currentRoute?.route} values={form.values} schema={currentRoute?.input} />}
</FormConsumer>
</Tab>
{
Object.entries(codeExamples).map(([title, comp]) => (
<Tab title={title} key={title}>
<FormConsumer>
{() => createElement(comp as FC<IClientProps>, {
path: currentRoute?.route,
values: transformData(form.values, currentRoute?.input),
schema: currentRoute?.input,
})}
</FormConsumer>
</Tab>
))
}
</Tabs>
</Form>
</FlexGridItem>
Expand Down
4 changes: 2 additions & 2 deletions webui/src/components/code/CURL.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)}' \\
Expand Down
45 changes: 40 additions & 5 deletions webui/src/hooks/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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<DataType, { [key: string]: any }>(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[] } = {}

Expand All @@ -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)
}
Expand All @@ -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<object>()
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))
Expand Down

0 comments on commit a619f51

Please sign in to comment.