From 04d1548290d799313ab7162a7ffb6a768c6a8251 Mon Sep 17 00:00:00 2001 From: SB-venkatyadavilli Date: Wed, 1 Apr 2026 15:33:06 +0530 Subject: [PATCH] fix: handled examples based on schema. --- .../exampleGeneration/exampleGeneration.ts | 151 +++++++++++++++++- 1 file changed, 146 insertions(+), 5 deletions(-) diff --git a/packages/elements-core/src/utils/exampleGeneration/exampleGeneration.ts b/packages/elements-core/src/utils/exampleGeneration/exampleGeneration.ts index 9603c3146..0ae448126 100644 --- a/packages/elements-core/src/utils/exampleGeneration/exampleGeneration.ts +++ b/packages/elements-core/src/utils/exampleGeneration/exampleGeneration.ts @@ -63,13 +63,44 @@ export const generateExampleFromMediaTypeContent = ( export const generateExamplesFromJsonSchema = (schema: JSONSchema7 & { 'x-examples'?: unknown }): Example[] => { const examples: Example[] = []; + const hasNoResolvedProperties = (schemaToCheck: JSONSchema7): boolean => { + // Cond1: object type with no properties + if (schemaToCheck.type !== 'object') return false; + + // Cond2: check allOf, oneOf, anyOf — if any sub-schema has 'properties', return false + const composedArray = schemaToCheck.allOf || schemaToCheck.oneOf || schemaToCheck.anyOf; + + if (Array.isArray(composedArray)) { + const hasProperties = composedArray.some(sub => typeof sub === 'object' && sub !== null && 'properties' in sub); + if (!hasProperties) { + return true; + } + } else if (!schemaToCheck.properties) { + return true; + } + + return false; + }; + + const isHasNoResolvedProperties = hasNoResolvedProperties(schema); + if (Array.isArray(schema?.examples)) { - schema.examples.forEach((example, index) => { - examples.push({ - data: safeStringify(example, undefined, 2) ?? '', - label: index === 0 ? 'default' : `example-${index}`, + if (isHasNoResolvedProperties) { + schema.examples.forEach((example, index) => { + examples.push({ + data: '{}', + label: index === 0 ? 'default' : `example-${index}`, + }); }); - }); + } else { + let res = filterExamplesBySchema(schema, schema.examples); + res.forEach((example, index) => { + examples.push({ + data: safeStringify(example, undefined, 2) ?? '', + label: index === 0 ? 'default' : `example-${index}`, + }); + }); + } } else if (isPlainObject(schema?.['x-examples'])) { for (const [label, example] of Object.entries(schema['x-examples'])) { if (isPlainObject(example)) { @@ -108,3 +139,113 @@ export const generateExamplesFromJsonSchema = (schema: JSONSchema7 & { 'x-exampl export const exceedsSize = (example: string, size: number = 500) => { return example.split(/\r\n|\r|\n/).length > size; }; + +/** + * Filters examples to only include properties that exist in the schema. + * Handles nested objects, arrays, allOf, oneOf, anyOf, and additionalProperties. + * Only removes a property from the example at the exact path where it was removed from the schema. + * + * @param schema - The JSON Schema (possibly with masked/hidden properties) + * @param examples - Array of raw JSON values (e.g. schema.examples) + * @returns New array of filtered objects matching the schema structure + */ +export const filterExamplesBySchema = ( + schema: JSONSchema7 & { 'x-examples'?: unknown }, + examples: unknown[], +): unknown[] => { + return examples.map(example => { + try { + return filterValueBySchema(example, schema); + } catch { + return example; + } + }); +}; + +const collectSchemaPropertyNames = (schema: JSONSchema7): Set => { + const keys = new Set(); + + if (schema.properties) { + for (const key of Object.keys(schema.properties)) { + keys.add(key); + } + } + + const composedSchemas = [ + ...(Array.isArray(schema.allOf) ? schema.allOf : []), + ...(Array.isArray(schema.oneOf) ? schema.oneOf : []), + ...(Array.isArray(schema.anyOf) ? schema.anyOf : []), + ]; + + for (const sub of composedSchemas) { + if (typeof sub === 'object' && sub !== null) { + for (const key of collectSchemaPropertyNames(sub as JSONSchema7)) { + keys.add(key); + } + } + } + + return keys; +}; + +const findPropertySchema = (schema: JSONSchema7, propertyName: string): JSONSchema7 | undefined => { + if (schema.properties?.[propertyName]) { + const prop = schema.properties[propertyName]; + return typeof prop === 'boolean' ? undefined : prop; + } + + const composedSchemas = [ + ...(Array.isArray(schema.allOf) ? schema.allOf : []), + ...(Array.isArray(schema.oneOf) ? schema.oneOf : []), + ...(Array.isArray(schema.anyOf) ? schema.anyOf : []), + ]; + + for (const sub of composedSchemas) { + if (typeof sub === 'object' && sub !== null) { + const found = findPropertySchema(sub as JSONSchema7, propertyName); + if (found) return found; + } + } + + return undefined; +}; + +const filterValueBySchema = (value: unknown, schema: JSONSchema7): unknown => { + if (value === null || value === undefined) return value; + + // Handle arrays + if (Array.isArray(value)) { + const itemSchema = + schema.items && typeof schema.items !== 'boolean' && !Array.isArray(schema.items) + ? (schema.items as JSONSchema7) + : undefined; + + return itemSchema ? value.map(item => filterValueBySchema(item, itemSchema)) : value; + } + + // Handle objects + if (isPlainObject(value)) { + const allowedKeys = collectSchemaPropertyNames(schema); + const hasStructure = allowedKeys.size > 0; + const hasAdditionalProperties = schema.additionalProperties; + + if (!hasStructure && !hasAdditionalProperties) return value; + + const result: Record = {}; + + for (const [key, val] of Object.entries(value as Record)) { + if (allowedKeys.has(key)) { + const propSchema = findPropertySchema(schema, key); + result[key] = propSchema ? filterValueBySchema(val, propSchema) : val; + } else if (hasAdditionalProperties) { + result[key] = val; + } + // else: property was masked/removed from schema — omit it + } + + return result; + } + + // Primitives + return value; +};