Skip to content

Commit 1579dc2

Browse files
committed
fix duplicated required properties validation, fixed required array preservation in schema output
1 parent 76eb88f commit 1579dc2

File tree

3 files changed

+60
-23
lines changed

3 files changed

+60
-23
lines changed

src/generateUISchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import {
66
DateTimeFormat,
77
ElementDisplay,
88
getFieldSetTitleKey,
9+
isEmptyString,
910
isFieldSet,
1011
isFieldSetTitleWithoutItems, isPropertyKey,
1112
isSchemaFieldSet, PropertyFormat,
1213
} from './utils/utils';
13-
import { isEmptyString } from './utils/stringUtils';
1414

1515
// Enums
1616
enum SchemaTypes {
@@ -57,7 +57,7 @@ export const generateUISchema = (schema: any) => {
5757
} else {
5858
Object.keys(schema.schema.properties).forEach((key: string) => {
5959
const element = getUIElement(key, schema);
60-
if (element) {
60+
if (!isEmpty(element)) {
6161
elements.push(element);
6262
}
6363
});

src/utils/utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,29 @@ export const isSchemaFieldSet = (definition: any[]) => {
9393

9494
export const isString = (item: any) => typeof item === STRING_TYPE;
9595

96+
// Helper function to recursively traverse and process the schema
97+
export const traverseSchema = (
98+
schema: any,
99+
processor: (node: any, path: string[]) => void,
100+
path: string[] = []
101+
) => {
102+
if (typeof schema !== 'object' || schema === null) {
103+
return;
104+
}
105+
106+
processor(schema, path);
107+
108+
if (Array.isArray(schema)) {
109+
schema.forEach((item, index) =>
110+
traverseSchema(item, processor, [...path, index.toString()])
111+
);
112+
} else {
113+
Object.entries(schema).forEach(([key, value]) =>
114+
traverseSchema(value, processor, [...path, key])
115+
);
116+
}
117+
};
118+
96119
//--------------------------------------------------------------
97120
// private functions
98121
//--------------------------------------------------------------

src/validateJsonSchema.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Internal Dependencies
2+
import { isEmpty } from 'lodash-es';
23
import {
34
ElementDisplay,
45
getFieldSetTitleKey,
56
getSchemaValidations,
67
hasDuplicatedItems,
78
HELP_VALUE,
8-
isArrayProperty,
99
isCheckbox,
1010
isDisabledChoice,
1111
isFieldSet,
@@ -17,6 +17,7 @@ import {
1717
isSchemaFieldSet,
1818
isString,
1919
REQUIRED_PROPERTY,
20+
traverseSchema,
2021
} from './utils/utils';
2122

2223
const specialCharactersInKey = /[^\w\n\s_"](?=[^:\n\s{}[]]*:[\t\n\s]*(\{|\[)+)/g;
@@ -59,24 +60,35 @@ const getTitleProperty = (title: string) => ({
5960
const getPropertyVisibility = (property: any) => property?.isHidden || false;
6061

6162
const cleanUpRequiredProperty = (schema: any) => {
62-
const requiredProperties = [];
63-
64-
// Iterate over the properties to get clean enum data
65-
for (const key of Object.keys(schema.properties)) {
66-
const property = schema.properties[key];
67-
if (isRequiredProperty(property)) {
68-
requiredProperties.push(key);
69-
delete schema.properties[key].required;
70-
}
71-
if (isArrayProperty(property)) {
72-
cleanUpRequiredProperty(property.items);
73-
}
74-
}
63+
// Store the original root-level required array if it exists
64+
const originalRequired = Array.isArray(schema.required) ? [...schema.required] : null;
7565

76-
if (requiredProperties.length > 0) {
77-
// eslint-disable-next-line no-param-reassign
78-
schema.required = requiredProperties;
79-
}
66+
traverseSchema(schema, (node, path) => {
67+
if (node.type === 'object' && node.properties) {
68+
const localRequired: string[] = [];
69+
70+
Object.entries(node.properties).forEach(([key, property]: [string, any]) => {
71+
if (isRequiredProperty(property)) {
72+
localRequired.push(key);
73+
delete property.required;
74+
}
75+
});
76+
77+
// Only modify required array if we found inline required properties
78+
if (!isEmpty(localRequired)) {
79+
// If this is the root schema and there was an original required array, merge them
80+
if (path.length === 0 && originalRequired) {
81+
const mergedRequired = [...originalRequired, ...localRequired];
82+
node.required = Array.from(new Set(mergedRequired));
83+
} else {
84+
node.required = localRequired;
85+
}
86+
} else if (path.length > 0 && node.required) {
87+
// Only delete required array for nested objects, not the root
88+
delete node.required;
89+
}
90+
}
91+
});
8092

8193
return schema;
8294
};
@@ -223,7 +235,7 @@ const validateSchema = (validations: any, schema: any) => {
223235
if (isInactiveChoice(property)) {
224236
schema.schema.properties[key].enum = cleanUpInactiveEnumChoice(property);
225237

226-
if (schema.schema.properties[key].enum.length === 0) {
238+
if (isEmpty(schema.schema.properties[key].enum.length)) {
227239
schema.schema.properties[key].enum = ['0'];
228240
schema.schema.properties[key].enumNames = { 0: 'No Options' };
229241
}
@@ -235,7 +247,8 @@ const validateSchema = (validations: any, schema: any) => {
235247
for (const key of Object.keys(schema.schema.properties)) {
236248
// Detect duplicated enum items
237249
if (
238-
schema.schema.properties[key].enum?.length > 0
250+
// schema.schema.properties[key].enum?.length > 0
251+
!isEmpty(schema.schema.properties[key].enum)
239252
&& hasDuplicatedItems(schema.schema.properties[key].enum)
240253
) {
241254
throw new Error('Duplicated enum items');
@@ -269,10 +282,11 @@ export const validateJSONSchema = (stringSchema: string) => {
269282
}
270283

271284
if (stringSchema.includes(REQUIRED_PROPERTY)) {
272-
schema.schema = cleanUpRequiredProperty(schema.schema);
285+
// Check for duplicated required properties in the original schema first
273286
if (schema.schema.required?.length > 0 && hasDuplicatedItems(schema.schema.required)) {
274287
throw new Error('Duplicated required properties');
275288
}
289+
schema.schema = cleanUpRequiredProperty(schema.schema);
276290
}
277291
return schema;
278292
};

0 commit comments

Comments
 (0)