Skip to content

Commit 04d1548

Browse files
fix: handled examples based on schema.
1 parent 6be709c commit 04d1548

1 file changed

Lines changed: 146 additions & 5 deletions

File tree

packages/elements-core/src/utils/exampleGeneration/exampleGeneration.ts

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,44 @@ export const generateExampleFromMediaTypeContent = (
6363
export const generateExamplesFromJsonSchema = (schema: JSONSchema7 & { 'x-examples'?: unknown }): Example[] => {
6464
const examples: Example[] = [];
6565

66+
const hasNoResolvedProperties = (schemaToCheck: JSONSchema7): boolean => {
67+
// Cond1: object type with no properties
68+
if (schemaToCheck.type !== 'object') return false;
69+
70+
// Cond2: check allOf, oneOf, anyOf — if any sub-schema has 'properties', return false
71+
const composedArray = schemaToCheck.allOf || schemaToCheck.oneOf || schemaToCheck.anyOf;
72+
73+
if (Array.isArray(composedArray)) {
74+
const hasProperties = composedArray.some(sub => typeof sub === 'object' && sub !== null && 'properties' in sub);
75+
if (!hasProperties) {
76+
return true;
77+
}
78+
} else if (!schemaToCheck.properties) {
79+
return true;
80+
}
81+
82+
return false;
83+
};
84+
85+
const isHasNoResolvedProperties = hasNoResolvedProperties(schema);
86+
6687
if (Array.isArray(schema?.examples)) {
67-
schema.examples.forEach((example, index) => {
68-
examples.push({
69-
data: safeStringify(example, undefined, 2) ?? '',
70-
label: index === 0 ? 'default' : `example-${index}`,
88+
if (isHasNoResolvedProperties) {
89+
schema.examples.forEach((example, index) => {
90+
examples.push({
91+
data: '{}',
92+
label: index === 0 ? 'default' : `example-${index}`,
93+
});
7194
});
72-
});
95+
} else {
96+
let res = filterExamplesBySchema(schema, schema.examples);
97+
res.forEach((example, index) => {
98+
examples.push({
99+
data: safeStringify(example, undefined, 2) ?? '',
100+
label: index === 0 ? 'default' : `example-${index}`,
101+
});
102+
});
103+
}
73104
} else if (isPlainObject(schema?.['x-examples'])) {
74105
for (const [label, example] of Object.entries(schema['x-examples'])) {
75106
if (isPlainObject(example)) {
@@ -108,3 +139,113 @@ export const generateExamplesFromJsonSchema = (schema: JSONSchema7 & { 'x-exampl
108139
export const exceedsSize = (example: string, size: number = 500) => {
109140
return example.split(/\r\n|\r|\n/).length > size;
110141
};
142+
143+
/**
144+
* Filters examples to only include properties that exist in the schema.
145+
* Handles nested objects, arrays, allOf, oneOf, anyOf, and additionalProperties.
146+
* Only removes a property from the example at the exact path where it was removed from the schema.
147+
*
148+
* @param schema - The JSON Schema (possibly with masked/hidden properties)
149+
* @param examples - Array of raw JSON values (e.g. schema.examples)
150+
* @returns New array of filtered objects matching the schema structure
151+
*/
152+
export const filterExamplesBySchema = (
153+
schema: JSONSchema7 & { 'x-examples'?: unknown },
154+
examples: unknown[],
155+
): unknown[] => {
156+
return examples.map(example => {
157+
try {
158+
return filterValueBySchema(example, schema);
159+
} catch {
160+
return example;
161+
}
162+
});
163+
};
164+
165+
const collectSchemaPropertyNames = (schema: JSONSchema7): Set<string> => {
166+
const keys = new Set<string>();
167+
168+
if (schema.properties) {
169+
for (const key of Object.keys(schema.properties)) {
170+
keys.add(key);
171+
}
172+
}
173+
174+
const composedSchemas = [
175+
...(Array.isArray(schema.allOf) ? schema.allOf : []),
176+
...(Array.isArray(schema.oneOf) ? schema.oneOf : []),
177+
...(Array.isArray(schema.anyOf) ? schema.anyOf : []),
178+
];
179+
180+
for (const sub of composedSchemas) {
181+
if (typeof sub === 'object' && sub !== null) {
182+
for (const key of collectSchemaPropertyNames(sub as JSONSchema7)) {
183+
keys.add(key);
184+
}
185+
}
186+
}
187+
188+
return keys;
189+
};
190+
191+
const findPropertySchema = (schema: JSONSchema7, propertyName: string): JSONSchema7 | undefined => {
192+
if (schema.properties?.[propertyName]) {
193+
const prop = schema.properties[propertyName];
194+
return typeof prop === 'boolean' ? undefined : prop;
195+
}
196+
197+
const composedSchemas = [
198+
...(Array.isArray(schema.allOf) ? schema.allOf : []),
199+
...(Array.isArray(schema.oneOf) ? schema.oneOf : []),
200+
...(Array.isArray(schema.anyOf) ? schema.anyOf : []),
201+
];
202+
203+
for (const sub of composedSchemas) {
204+
if (typeof sub === 'object' && sub !== null) {
205+
const found = findPropertySchema(sub as JSONSchema7, propertyName);
206+
if (found) return found;
207+
}
208+
}
209+
210+
return undefined;
211+
};
212+
213+
const filterValueBySchema = (value: unknown, schema: JSONSchema7): unknown => {
214+
if (value === null || value === undefined) return value;
215+
216+
// Handle arrays
217+
if (Array.isArray(value)) {
218+
const itemSchema =
219+
schema.items && typeof schema.items !== 'boolean' && !Array.isArray(schema.items)
220+
? (schema.items as JSONSchema7)
221+
: undefined;
222+
223+
return itemSchema ? value.map(item => filterValueBySchema(item, itemSchema)) : value;
224+
}
225+
226+
// Handle objects
227+
if (isPlainObject(value)) {
228+
const allowedKeys = collectSchemaPropertyNames(schema);
229+
const hasStructure = allowedKeys.size > 0;
230+
const hasAdditionalProperties = schema.additionalProperties;
231+
232+
if (!hasStructure && !hasAdditionalProperties) return value;
233+
234+
const result: Record<string, unknown> = {};
235+
236+
for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
237+
if (allowedKeys.has(key)) {
238+
const propSchema = findPropertySchema(schema, key);
239+
result[key] = propSchema ? filterValueBySchema(val, propSchema) : val;
240+
} else if (hasAdditionalProperties) {
241+
result[key] = val;
242+
}
243+
// else: property was masked/removed from schema — omit it
244+
}
245+
246+
return result;
247+
}
248+
249+
// Primitives
250+
return value;
251+
};

0 commit comments

Comments
 (0)