diff --git a/components/openapi/api-playground/index.tsx b/components/openapi/api-playground/index.tsx index ed7f67213..4d8a184d8 100644 --- a/components/openapi/api-playground/index.tsx +++ b/components/openapi/api-playground/index.tsx @@ -14,6 +14,7 @@ import { useApiCredentials } from '@/providers/api-credentials-provider'; import type { OpenAPIOperation } from '../types'; import { RequestBuilder } from './request-builder'; import { executeRequest } from './request-executor'; +import { coerceValueForSchema } from './schema-utils'; interface APIPlaygroundProps { operation: OpenAPIOperation; @@ -230,6 +231,8 @@ export function APIPlayground({ } let finalFormData = { ...formData }; + let bodyFieldErrors: string[] = []; + if (operation.requestBody) { const bodySchema = operation.requestBody.content?.['application/json']?.schema; if (bodySchema?.type === 'object' && bodySchema.properties) { @@ -294,18 +297,39 @@ export function APIPlayground({ }); } catch (error) { console.error('Failed to convert arguments:', error); - bodyObject[propName] = fieldValue; + bodyFieldErrors.push( + error instanceof Error ? error.message : `Invalid value provided for ${propName}`, + ); } } else if (propName === 'sender') { // Sender stays as string bodyObject[propName] = fieldValue; } else { - // Other fields - no conversion for now - bodyObject[propName] = fieldValue; + try { + bodyObject[propName] = coerceValueForSchema(fieldValue, propSchema, { + strict: true, + fieldName: propName, + }); + } catch (error) { + bodyFieldErrors.push( + error instanceof Error ? error.message : `Invalid value provided for ${propName}`, + ); + } } } } + if (bodyFieldErrors.length > 0) { + if (!openSections.includes('body')) { + setOpenSections((prev) => [...prev, 'body']); + } + setResponse({ + status: 0, + error: bodyFieldErrors[0], + }); + return; + } + finalFormData = { ...finalFormData, body: JSON.stringify(bodyObject, null, 2), diff --git a/components/openapi/api-playground/request-builder.tsx b/components/openapi/api-playground/request-builder.tsx index 0ad8ee6b1..7fbb0a5eb 100644 --- a/components/openapi/api-playground/request-builder.tsx +++ b/components/openapi/api-playground/request-builder.tsx @@ -10,6 +10,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp import { cn } from '@/lib/utils'; import type { OpenAPIOperation, OpenAPIParameter } from '../types'; import { ClarityConverter, type ClarityTypeHint } from './clarity-converter'; +import { coerceValueForSchema, resolveEffectiveSchema } from './schema-utils'; interface RequestBuilderProps { operation: OpenAPIOperation; @@ -135,12 +136,14 @@ export const RequestBuilder = forwardRef( const bodySchema = operation.requestBody.content?.['application/json']?.schema; if (bodySchema?.type === 'object' && bodySchema.properties) { const bodyObject: Record = {}; + const parseErrors: Record = {}; for (const [propName, propSchema] of Object.entries(bodySchema.properties) as [ string, any, ][]) { const fieldValue = formData[`body.${propName}`]; + const fieldName = `body.${propName}`; if (fieldValue !== undefined && fieldValue !== '') { if (clarityConversion) { if (propName === 'arguments' && propSchema.type === 'array') { @@ -152,7 +155,10 @@ export const RequestBuilder = forwardRef( }); } catch (error) { console.error('Failed to convert arguments:', error); - bodyObject[propName] = fieldValue; + parseErrors[fieldName] = + error instanceof Error + ? error.message + : `Invalid value provided for ${propName}`; } } else { if (propName === 'sender') { @@ -167,19 +173,54 @@ export const RequestBuilder = forwardRef( ); bodyObject[propName] = cvToHex(clarityValue); } catch (error) { - bodyObject[propName] = fieldValue; + try { + bodyObject[propName] = coerceValueForSchema(fieldValue, propSchema, { + strict: true, + fieldName: propName, + }); + } catch (coerceError) { + parseErrors[fieldName] = + coerceError instanceof Error + ? coerceError.message + : `Invalid value provided for ${propName}`; + } } } else { - bodyObject[propName] = fieldValue; + try { + bodyObject[propName] = coerceValueForSchema(fieldValue, propSchema, { + strict: true, + fieldName: propName, + }); + } catch (error) { + parseErrors[fieldName] = + error instanceof Error + ? error.message + : `Invalid value provided for ${propName}`; + } } } } } else { - bodyObject[propName] = fieldValue; + try { + bodyObject[propName] = coerceValueForSchema(fieldValue, propSchema, { + strict: true, + fieldName: propName, + }); + } catch (error) { + parseErrors[fieldName] = + error instanceof Error + ? error.message + : `Invalid value provided for ${propName}`; + } } } } + if (Object.keys(parseErrors).length > 0) { + setErrors((prev) => ({ ...prev, ...parseErrors })); + return; + } + finalFormData = { ...finalFormData, body: JSON.stringify(bodyObject, null, 2), @@ -422,9 +463,26 @@ export const RequestBuilder = forwardRef( const fieldName = `body.${propName}`; const isRequired = bodySchema.required?.includes(propName); const hasError = !!errors[fieldName]; + const resolvedPropSchema = resolveEffectiveSchema(propSchema) || propSchema; + const schemaType = resolvedPropSchema?.type || propSchema.type; const clarityType = clarityConversion ? detectClarityType(propName, propSchema, formData[fieldName] || '') : null; + const exampleValue = + typeof propSchema.example === 'string' + ? propSchema.example + : propSchema.example + ? JSON.stringify(propSchema.example, null, 2) + : undefined; + const placeholder = + exampleValue || + propSchema.description || + (schemaType === 'object' + ? 'Enter JSON object' + : schemaType === 'array' + ? 'Enter array values as JSON array' + : undefined); + const shouldUseTextarea = schemaType === 'array' || schemaType === 'object'; return (
@@ -451,19 +509,17 @@ export const RequestBuilder = forwardRef( )}
- {propSchema.type === 'array' ? ( + {shouldUseTextarea ? (