Skip to content

Commit 576d9e0

Browse files
kchobantonovsdirix
andauthored
feat: check combinators in createDefaultValue
Adjusts createDefaultValue to also check for values in combinators, i.e. oneOf, anyOf and allOf. Co-authored-by: Stefan Dirix <sdirix@eclipsesource.com>
1 parent 4a4e2ec commit 576d9e0

File tree

2 files changed

+208
-12
lines changed

2 files changed

+208
-12
lines changed

packages/core/src/mappers/renderer.ts

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,25 @@ export const createDefaultValue = (
170170
schema: JsonSchema,
171171
rootSchema: JsonSchema
172172
) => {
173-
const resolvedSchema = Resolve.schema(schema, schema.$ref, rootSchema);
173+
const defaultValue = doCreateDefaultValue(schema, rootSchema);
174+
175+
// preserve the backward compatibility where it is returning an empty object if we can't determine the default value
176+
return defaultValue === undefined ? {} : defaultValue;
177+
};
178+
179+
/**
180+
* Create a default value based on the given schema.
181+
* @param schema the schema for which to create a default value.
182+
* @returns the default value to use, undefined if none was found
183+
*/
184+
export const doCreateDefaultValue = (
185+
schema: JsonSchema,
186+
rootSchema: JsonSchema
187+
) => {
188+
const resolvedSchema =
189+
typeof schema.$ref === 'string'
190+
? Resolve.schema(rootSchema, schema.$ref, rootSchema)
191+
: schema;
174192
if (resolvedSchema.default !== undefined) {
175193
return extractDefaults(resolvedSchema, rootSchema);
176194
}
@@ -183,22 +201,56 @@ export const createDefaultValue = (
183201
return convertDateToString(new Date(), resolvedSchema.format);
184202
}
185203
return '';
186-
} else if (
187-
hasType(resolvedSchema, 'integer') ||
188-
hasType(resolvedSchema, 'number')
189-
) {
204+
}
205+
if (hasType(resolvedSchema, 'integer') || hasType(resolvedSchema, 'number')) {
190206
return 0;
191-
} else if (hasType(resolvedSchema, 'boolean')) {
207+
}
208+
if (hasType(resolvedSchema, 'boolean')) {
192209
return false;
193-
} else if (hasType(resolvedSchema, 'array')) {
210+
}
211+
if (hasType(resolvedSchema, 'array')) {
194212
return [];
195-
} else if (hasType(resolvedSchema, 'object')) {
213+
}
214+
if (hasType(resolvedSchema, 'object')) {
196215
return extractDefaults(resolvedSchema, rootSchema);
197-
} else if (hasType(resolvedSchema, 'null')) {
216+
}
217+
if (hasType(resolvedSchema, 'null')) {
198218
return null;
199-
} else {
200-
return {};
201219
}
220+
221+
const combinators: CombinatorKeyword[] = ['oneOf', 'anyOf', 'allOf'];
222+
for (const combinator of combinators) {
223+
if (schema[combinator] && Array.isArray(schema[combinator])) {
224+
const combinatorDefault = createDefaultValueForCombinatorSchema(
225+
schema[combinator],
226+
rootSchema
227+
);
228+
if (combinatorDefault !== undefined) {
229+
return combinatorDefault;
230+
}
231+
}
232+
}
233+
234+
// no default value found
235+
return undefined;
236+
};
237+
238+
const createDefaultValueForCombinatorSchema = (
239+
combinatorSchemas: JsonSchema[],
240+
rootSchema: JsonSchema
241+
): any => {
242+
if (combinatorSchemas.length > 0) {
243+
for (const combinatorSchema of combinatorSchemas) {
244+
const result = doCreateDefaultValue(combinatorSchema, rootSchema);
245+
if (result !== undefined) {
246+
// return the first one with type information
247+
return result;
248+
}
249+
}
250+
}
251+
252+
// no default value found
253+
return undefined;
202254
};
203255

204256
/**
@@ -214,10 +266,26 @@ export const extractDefaults = (schema: JsonSchema, rootSchema: JsonSchema) => {
214266
const resolvedProperty = property.$ref
215267
? Resolve.schema(rootSchema, property.$ref, rootSchema)
216268
: property;
217-
if (resolvedProperty.default !== undefined) {
269+
if (resolvedProperty && resolvedProperty.default !== undefined) {
218270
result[key] = cloneDeep(resolvedProperty.default);
219271
}
220272
}
273+
// there could be more properties in allOf schemas
274+
if (schema.allOf && Array.isArray(schema.allOf)) {
275+
schema.allOf.forEach((allOfSchema) => {
276+
if (allOfSchema && allOfSchema.properties) {
277+
for (const key in allOfSchema.properties) {
278+
const property = allOfSchema.properties[key];
279+
const resolvedProperty = property.$ref
280+
? Resolve.schema(rootSchema, property.$ref, rootSchema)
281+
: property;
282+
if (resolvedProperty && resolvedProperty.default !== undefined) {
283+
result[key] = cloneDeep(resolvedProperty.default);
284+
}
285+
}
286+
}
287+
});
288+
}
221289
return result;
222290
}
223291
return cloneDeep(schema.default);

packages/core/test/mappers/renderer.test.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,134 @@ test('createDefaultValue', (t) => {
654654
bool: true,
655655
array: ['a', 'b', 'c'],
656656
});
657+
658+
const schemaOneOf: JsonSchema = {
659+
oneOf: [
660+
{
661+
type: 'string',
662+
default: 'oneOfString',
663+
},
664+
{
665+
type: 'number',
666+
default: 42,
667+
},
668+
],
669+
};
670+
const rootSchemaOneOf: JsonSchema = {
671+
definitions: {},
672+
};
673+
const defaultValueOneOf = createDefaultValue(schemaOneOf, rootSchemaOneOf);
674+
t.is(defaultValueOneOf, 'oneOfString');
675+
676+
const schemaAnyOf: JsonSchema = {
677+
anyOf: [
678+
{
679+
type: 'number',
680+
},
681+
{
682+
type: 'string',
683+
default: 'anyOfString',
684+
},
685+
],
686+
};
687+
const rootSchemaAnyOf: JsonSchema = {
688+
definitions: {},
689+
};
690+
const defaultValueAnyOf = createDefaultValue(schemaAnyOf, rootSchemaAnyOf);
691+
t.is(defaultValueAnyOf, 0);
692+
693+
console.log('testcase allof');
694+
const schemaAllOf: JsonSchema = {
695+
allOf: [
696+
{
697+
properties: {
698+
foo: {
699+
type: 'string',
700+
default: 'foo',
701+
},
702+
},
703+
},
704+
{
705+
properties: {
706+
bar: {
707+
type: 'number',
708+
default: 42,
709+
},
710+
},
711+
},
712+
],
713+
};
714+
const rootSchemaAllOf: JsonSchema = {
715+
definitions: {},
716+
};
717+
const defaultValueAllOf = createDefaultValue(schemaAllOf, rootSchemaAllOf);
718+
t.deepEqual(defaultValueAllOf, { foo: 'foo', bar: 42 });
719+
720+
const schemaOneOfEmpty: JsonSchema = {
721+
oneOf: [
722+
{
723+
type: 'string',
724+
},
725+
{
726+
type: 'number',
727+
},
728+
],
729+
};
730+
const rootSchemaOneOfEmpty: JsonSchema = {
731+
definitions: {},
732+
};
733+
const defaultValueOneOfEmpty = createDefaultValue(
734+
schemaOneOfEmpty,
735+
rootSchemaOneOfEmpty
736+
);
737+
t.deepEqual(defaultValueOneOfEmpty, '');
738+
739+
const schemaAnyOfEmpty: JsonSchema = {
740+
anyOf: [
741+
{
742+
type: 'string',
743+
},
744+
{
745+
type: 'number',
746+
},
747+
],
748+
};
749+
const rootSchemaAnyOfEmpty: JsonSchema = {
750+
definitions: {},
751+
};
752+
const defaultValueAnyOfEmpty = createDefaultValue(
753+
schemaAnyOfEmpty,
754+
rootSchemaAnyOfEmpty
755+
);
756+
t.deepEqual(defaultValueAnyOfEmpty, '');
757+
758+
const schemaAllOfEmpty: JsonSchema = {
759+
allOf: [
760+
{
761+
properties: {
762+
foo: {
763+
type: 'string',
764+
},
765+
},
766+
},
767+
{
768+
properties: {
769+
bar: {
770+
type: 'number',
771+
},
772+
},
773+
},
774+
],
775+
};
776+
const rootSchemaAllOfEmpty: JsonSchema = {
777+
definitions: {},
778+
};
779+
const defaultValueAllOfEmpty = createDefaultValue(
780+
schemaAllOfEmpty,
781+
rootSchemaAllOfEmpty
782+
);
783+
console.log('defaultValueAllOfEmpty', defaultValueAllOfEmpty);
784+
t.deepEqual(defaultValueAllOfEmpty, {});
657785
});
658786

659787
test(`mapStateToJsonFormsRendererProps should use registered UI schema given ownProps schema`, (t) => {

0 commit comments

Comments
 (0)