Skip to content

Commit

Permalink
feat(@angular-devkit/core): add basic support for oneOf/anyOf to `add…
Browse files Browse the repository at this point in the history
…UndefinedDefaults` transformer
  • Loading branch information
alan-agius4 committed Sep 25, 2020
1 parent 0c112e5 commit 1b8fabf
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 104 deletions.
95 changes: 0 additions & 95 deletions packages/angular_devkit/core/src/json/schema/registry_spec.ts
Expand Up @@ -386,101 +386,6 @@ describe('CoreSchemaRegistry', () => {
expect(result.data).toBe(data);
});

it('adds undefined properties', done => {
const registry = new CoreSchemaRegistry();
registry.addPostTransform(addUndefinedDefaults);
const data: any = {}; // tslint:disable-line:no-any

registry
.compile({
properties: {
bool: { type: 'boolean' },
str: { type: 'string', default: 'someString' },
obj: {
properties: {
num: { type: 'number' },
other: { type: 'number', default: 0 },
},
},
objAllOk: {
allOf: [
{ type: 'object' },
],
},
objAllBad: {
allOf: [
{ type: 'object' },
{ type: 'number' },
],
},
objOne: {
oneOf: [
{ type: 'object' },
],
},
objNotOk: {
not: { not: { type: 'object' } },
},
objNotBad: {
type: 'object',
not: { type: 'object' },
},
},
})
.pipe(
mergeMap(validator => validator(data)),
map(result => {
expect(result.success).toBe(true);
expect(data.bool).toBeUndefined();
expect(data.str).toBe('someString');
expect(data.obj.num).toBeUndefined();
expect(data.obj.other).toBe(0);
expect(data.objAllOk).toEqual({});
expect(data.objOne).toEqual({});
expect(data.objAllBad).toBeUndefined();
expect(data.objNotOk).toEqual({});
expect(data.objNotBad).toBeUndefined();
}),
)
.toPromise().then(done, done.fail);
});

it('adds defaults to undefined properties', done => {
const registry = new CoreSchemaRegistry();
registry.addPostTransform(addUndefinedDefaults);
// tslint:disable-line:no-any
const data: any = {
bool: undefined,
str: undefined,
obj: {
num: undefined,
},
};

registry
.compile({
properties: {
bool: { type: 'boolean', default: true },
str: { type: 'string', default: 'someString' },
obj: {
properties: {
num: { type: 'number', default: 0 },
},
},
},
})
.pipe(
mergeMap(validator => validator(data)),
map(result => {
expect(result.success).toBe(true);
expect(data.bool).toBe(true);
expect(data.str).toBe('someString');
expect(data.obj.num).toBe(0);
}),
)
.toPromise().then(done, done.fail);
});

it('adds deprecated options usage', done => {
const registry = new CoreSchemaRegistry();
const deprecatedMessages: string[] = [];
Expand Down
38 changes: 29 additions & 9 deletions packages/angular_devkit/core/src/json/schema/transforms.ts
Expand Up @@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { JsonObject, JsonValue, isJsonObject } from '../interface';
import { JsonObject, JsonValue, isJsonArray, isJsonObject } from '../interface';
import { JsonPointer } from './interface';
import { JsonSchema } from './schema';
import { getTypesOfSchema } from './utility';
Expand All @@ -15,10 +15,7 @@ export function addUndefinedDefaults(
_pointer: JsonPointer,
schema?: JsonSchema,
): JsonValue {
if (schema === true || schema === false) {
return value;
}
if (schema === undefined) {
if (typeof schema === 'boolean' || schema === undefined) {
return value;
}

Expand Down Expand Up @@ -64,14 +61,37 @@ export function addUndefinedDefaults(
}

for (const [propName, schemaObject] of Object.entries(schema.properties)) {
if (newValue[propName] !== undefined || propName === '$schema') {
if (propName === '$schema' || !isJsonObject(schemaObject)) {
continue;
}

// TODO: Does not currently handle more complex schemas (oneOf/anyOf/etc.)
const defaultValue = (schemaObject as JsonObject).default;
const value = newValue[propName];
if (value === undefined) {
newValue[propName] = schemaObject.default;
} else if (isJsonObject(value)) {
// Basic support for oneOf and anyOf.
const propertySchemas = schemaObject.oneOf || schemaObject.anyOf;
const allProperties = Object.keys(value);
// Locate a schema which declares all the properties that the object contains.
const adjustedSchema = isJsonArray(propertySchemas) && propertySchemas.find(s => {
if (!isJsonObject(s)) {
return false;
}

const schemaType = getTypesOfSchema(s);
if (schemaType.size === 1 && schemaType.has('object') && isJsonObject(s.properties)) {
const properties = Object.keys(s.properties);

newValue[propName] = defaultValue;
return allProperties.every(key => properties.includes(key));
}

return false;
});

if (adjustedSchema && isJsonObject(adjustedSchema)) {
newValue[propName] = addUndefinedDefaults(value, _pointer, adjustedSchema);
}
}
}

return newValue;
Expand Down

0 comments on commit 1b8fabf

Please sign in to comment.