Skip to content

Commit

Permalink
feat: check combinators in createDefaultValue
Browse files Browse the repository at this point in the history
Adjusts createDefaultValue to also check for values in combinators,
i.e. oneOf, anyOf and allOf.

Co-authored-by: Stefan Dirix <sdirix@eclipsesource.com>
  • Loading branch information
kchobantonov and sdirix authored Dec 6, 2024
1 parent 4a4e2ec commit 576d9e0
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 12 deletions.
92 changes: 80 additions & 12 deletions packages/core/src/mappers/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,25 @@ export const createDefaultValue = (
schema: JsonSchema,
rootSchema: JsonSchema
) => {
const resolvedSchema = Resolve.schema(schema, schema.$ref, rootSchema);
const defaultValue = doCreateDefaultValue(schema, rootSchema);

// preserve the backward compatibility where it is returning an empty object if we can't determine the default value
return defaultValue === undefined ? {} : defaultValue;
};

/**
* Create a default value based on the given schema.
* @param schema the schema for which to create a default value.
* @returns the default value to use, undefined if none was found
*/
export const doCreateDefaultValue = (
schema: JsonSchema,
rootSchema: JsonSchema
) => {
const resolvedSchema =
typeof schema.$ref === 'string'
? Resolve.schema(rootSchema, schema.$ref, rootSchema)
: schema;
if (resolvedSchema.default !== undefined) {
return extractDefaults(resolvedSchema, rootSchema);
}
Expand All @@ -183,22 +201,56 @@ export const createDefaultValue = (
return convertDateToString(new Date(), resolvedSchema.format);
}
return '';
} else if (
hasType(resolvedSchema, 'integer') ||
hasType(resolvedSchema, 'number')
) {
}
if (hasType(resolvedSchema, 'integer') || hasType(resolvedSchema, 'number')) {
return 0;
} else if (hasType(resolvedSchema, 'boolean')) {
}
if (hasType(resolvedSchema, 'boolean')) {
return false;
} else if (hasType(resolvedSchema, 'array')) {
}
if (hasType(resolvedSchema, 'array')) {
return [];
} else if (hasType(resolvedSchema, 'object')) {
}
if (hasType(resolvedSchema, 'object')) {
return extractDefaults(resolvedSchema, rootSchema);
} else if (hasType(resolvedSchema, 'null')) {
}
if (hasType(resolvedSchema, 'null')) {
return null;
} else {
return {};
}

const combinators: CombinatorKeyword[] = ['oneOf', 'anyOf', 'allOf'];
for (const combinator of combinators) {
if (schema[combinator] && Array.isArray(schema[combinator])) {
const combinatorDefault = createDefaultValueForCombinatorSchema(
schema[combinator],
rootSchema
);
if (combinatorDefault !== undefined) {
return combinatorDefault;
}
}
}

// no default value found
return undefined;
};

const createDefaultValueForCombinatorSchema = (
combinatorSchemas: JsonSchema[],
rootSchema: JsonSchema
): any => {
if (combinatorSchemas.length > 0) {
for (const combinatorSchema of combinatorSchemas) {
const result = doCreateDefaultValue(combinatorSchema, rootSchema);
if (result !== undefined) {
// return the first one with type information
return result;
}
}
}

// no default value found
return undefined;
};

/**
Expand All @@ -214,10 +266,26 @@ export const extractDefaults = (schema: JsonSchema, rootSchema: JsonSchema) => {
const resolvedProperty = property.$ref
? Resolve.schema(rootSchema, property.$ref, rootSchema)
: property;
if (resolvedProperty.default !== undefined) {
if (resolvedProperty && resolvedProperty.default !== undefined) {
result[key] = cloneDeep(resolvedProperty.default);
}
}
// there could be more properties in allOf schemas
if (schema.allOf && Array.isArray(schema.allOf)) {
schema.allOf.forEach((allOfSchema) => {
if (allOfSchema && allOfSchema.properties) {
for (const key in allOfSchema.properties) {
const property = allOfSchema.properties[key];
const resolvedProperty = property.$ref
? Resolve.schema(rootSchema, property.$ref, rootSchema)
: property;
if (resolvedProperty && resolvedProperty.default !== undefined) {
result[key] = cloneDeep(resolvedProperty.default);
}
}
}
});
}
return result;
}
return cloneDeep(schema.default);
Expand Down
128 changes: 128 additions & 0 deletions packages/core/test/mappers/renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,134 @@ test('createDefaultValue', (t) => {
bool: true,
array: ['a', 'b', 'c'],
});

const schemaOneOf: JsonSchema = {
oneOf: [
{
type: 'string',
default: 'oneOfString',
},
{
type: 'number',
default: 42,
},
],
};
const rootSchemaOneOf: JsonSchema = {
definitions: {},
};
const defaultValueOneOf = createDefaultValue(schemaOneOf, rootSchemaOneOf);
t.is(defaultValueOneOf, 'oneOfString');

const schemaAnyOf: JsonSchema = {
anyOf: [
{
type: 'number',
},
{
type: 'string',
default: 'anyOfString',
},
],
};
const rootSchemaAnyOf: JsonSchema = {
definitions: {},
};
const defaultValueAnyOf = createDefaultValue(schemaAnyOf, rootSchemaAnyOf);
t.is(defaultValueAnyOf, 0);

console.log('testcase allof');
const schemaAllOf: JsonSchema = {
allOf: [
{
properties: {
foo: {
type: 'string',
default: 'foo',
},
},
},
{
properties: {
bar: {
type: 'number',
default: 42,
},
},
},
],
};
const rootSchemaAllOf: JsonSchema = {
definitions: {},
};
const defaultValueAllOf = createDefaultValue(schemaAllOf, rootSchemaAllOf);
t.deepEqual(defaultValueAllOf, { foo: 'foo', bar: 42 });

const schemaOneOfEmpty: JsonSchema = {
oneOf: [
{
type: 'string',
},
{
type: 'number',
},
],
};
const rootSchemaOneOfEmpty: JsonSchema = {
definitions: {},
};
const defaultValueOneOfEmpty = createDefaultValue(
schemaOneOfEmpty,
rootSchemaOneOfEmpty
);
t.deepEqual(defaultValueOneOfEmpty, '');

const schemaAnyOfEmpty: JsonSchema = {
anyOf: [
{
type: 'string',
},
{
type: 'number',
},
],
};
const rootSchemaAnyOfEmpty: JsonSchema = {
definitions: {},
};
const defaultValueAnyOfEmpty = createDefaultValue(
schemaAnyOfEmpty,
rootSchemaAnyOfEmpty
);
t.deepEqual(defaultValueAnyOfEmpty, '');

const schemaAllOfEmpty: JsonSchema = {
allOf: [
{
properties: {
foo: {
type: 'string',
},
},
},
{
properties: {
bar: {
type: 'number',
},
},
},
],
};
const rootSchemaAllOfEmpty: JsonSchema = {
definitions: {},
};
const defaultValueAllOfEmpty = createDefaultValue(
schemaAllOfEmpty,
rootSchemaAllOfEmpty
);
console.log('defaultValueAllOfEmpty', defaultValueAllOfEmpty);
t.deepEqual(defaultValueAllOfEmpty, {});
});

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

0 comments on commit 576d9e0

Please sign in to comment.