Skip to content

Commit

Permalink
refactor: add CommonModel conversion to MetaModel (#677)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored Mar 9, 2022
1 parent 02fbb67 commit e34866b
Show file tree
Hide file tree
Showing 3 changed files with 374 additions and 0 deletions.
202 changes: 202 additions & 0 deletions src/helpers/CommonModelToMetaModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@

import { CommonModel, MetaModel, UnionModel, ObjectModel, DictionaryModel, StringModel, TupleModel, TupleValueModel, ArrayModel, BooleanModel, IntegerModel, FloatModel, EnumModel, EnumValueModel} from '../models';

export function convertToMetaModel(jsonSchemaModel: CommonModel): MetaModel {
const modelName = jsonSchemaModel.$id || 'undefined';

const unionModel = convertToUnionModel(jsonSchemaModel, modelName);
if (unionModel !== undefined) {
return unionModel;
}
const stringModel = convertToStringModel(jsonSchemaModel, modelName);
if (stringModel !== undefined) {
return stringModel;
}
const floatModel = convertToFloatModel(jsonSchemaModel, modelName);
if (floatModel !== undefined) {
return floatModel;
}
const integerModel = convertToIntegerModel(jsonSchemaModel, modelName);
if (integerModel !== undefined) {
return integerModel;
}
const enumModel = convertToEnumModel(jsonSchemaModel, modelName);
if (enumModel !== undefined) {
return enumModel;
}
const booleanModel = convertToBooleanModel(jsonSchemaModel, modelName);
if (booleanModel !== undefined) {
return booleanModel;
}
const objectModel = convertToObjectModel(jsonSchemaModel, modelName);
if (objectModel !== undefined) {
return objectModel;
}
const tupleModel = convertToTupleModel(jsonSchemaModel, modelName);
if (tupleModel !== undefined) {
return tupleModel;
}
const arrayModel = convertToArrayModel(jsonSchemaModel, modelName);
if (arrayModel !== undefined) {
return arrayModel;
}
throw new Error('Failed to convert to MetaModel');
}
export function convertToUnionModel(jsonSchemaModel: CommonModel, name: string): UnionModel | undefined {
if (!Array.isArray(jsonSchemaModel.type) || jsonSchemaModel.type.length <= 1) {
return undefined;
}
// Has multiple types, so convert to union
const unionModel = new UnionModel(name, jsonSchemaModel.originalInput, []);
const stringModel = convertToStringModel(jsonSchemaModel, name);
if (stringModel !== undefined) {
unionModel.union.push(stringModel);
}
const floatModel = convertToFloatModel(jsonSchemaModel, name);
if (floatModel !== undefined) {
unionModel.union.push(floatModel);
}
const integerModel = convertToIntegerModel(jsonSchemaModel, name);
if (integerModel !== undefined) {
unionModel.union.push(integerModel);
}
const enumModel = convertToEnumModel(jsonSchemaModel, name);
if (enumModel !== undefined) {
unionModel.union.push(enumModel);
}
const booleanModel = convertToBooleanModel(jsonSchemaModel, name);
if (booleanModel !== undefined) {
unionModel.union.push(booleanModel);
}
const objectModel = convertToObjectModel(jsonSchemaModel, name);
if (objectModel !== undefined) {
unionModel.union.push(objectModel);
}
const tupleModel = convertToTupleModel(jsonSchemaModel, name);
if (tupleModel !== undefined) {
unionModel.union.push(tupleModel);
}
const arrayModel = convertToArrayModel(jsonSchemaModel, name);
if (arrayModel !== undefined) {
unionModel.union.push(arrayModel);
}
return unionModel;
}
export function convertToStringModel(jsonSchemaModel: CommonModel, name: string): StringModel | undefined {
if (!jsonSchemaModel.type?.includes('string')) {
return undefined;
}
return new StringModel(name, jsonSchemaModel.originalInput);
}
export function convertToIntegerModel(jsonSchemaModel: CommonModel, name: string): IntegerModel | undefined {
if (!jsonSchemaModel.type?.includes('integer')) {
return undefined;
}
return new IntegerModel(name, jsonSchemaModel.originalInput);
}
export function convertToFloatModel(jsonSchemaModel: CommonModel, name: string): FloatModel | undefined {
if (!jsonSchemaModel.type?.includes('number')) {
return undefined;
}
return new FloatModel(name, jsonSchemaModel.originalInput);
}
export function convertToEnumModel(jsonSchemaModel: CommonModel, name: string): EnumModel | undefined {
if (!Array.isArray(jsonSchemaModel.enum)) {
return undefined;
}
const metaModel = new EnumModel(name, jsonSchemaModel.originalInput, []);
for (const enumValue of jsonSchemaModel.enum) {
const enumValueModel = new EnumValueModel(JSON.stringify(enumValue), enumValue);
metaModel.values.push(enumValueModel);
}
return metaModel;
}
export function convertToBooleanModel(jsonSchemaModel: CommonModel, name: string): BooleanModel | undefined {
if (!jsonSchemaModel.type?.includes('boolean')) {
return undefined;
}
return new BooleanModel(name, jsonSchemaModel.originalInput);
}
export function convertToObjectModel(jsonSchemaModel: CommonModel, name: string): ObjectModel | undefined {
if (!jsonSchemaModel.type?.includes('object')) {
return undefined;
}
const metaModel = new ObjectModel(name, jsonSchemaModel.originalInput, {});
for (const [propertyName, prop] of Object.entries(jsonSchemaModel.properties || {})) {
metaModel.properties[String(propertyName)] = convertToMetaModel(prop);
}

if (jsonSchemaModel.additionalProperties !== undefined) {
const propertyName = 'additionalProperties';
if (metaModel.properties[String(propertyName)] === undefined) {
const keyModel = new StringModel(propertyName, jsonSchemaModel.additionalProperties.originalInput);
const valueModel = convertToMetaModel(jsonSchemaModel.additionalProperties);
const dictionaryModel = new DictionaryModel(propertyName, jsonSchemaModel.additionalProperties.originalInput, keyModel, valueModel, 'unwrap');
metaModel.properties[String(propertyName)] = dictionaryModel;
} else {
throw new Error('Property already exists');
}
}

if (jsonSchemaModel.patternProperties !== undefined) {
for (const [pattern, patternModel] of Object.entries(jsonSchemaModel.patternProperties)) {
const propertyName = `${pattern}_PatternProperty`;
if (metaModel.properties[String(propertyName)] === undefined) {
const keyModel = new StringModel(propertyName, pattern);
const valueModel = convertToMetaModel(patternModel);
const dictionaryModel = new DictionaryModel(propertyName, patternModel.originalInput, keyModel, valueModel, 'unwrap');
metaModel.properties[String(propertyName)] = dictionaryModel;
} else {
throw new Error('Property already exists');
}
}
}
return metaModel;
}

export function convertToArrayModel(jsonSchemaModel: CommonModel, name: string): ArrayModel | undefined {
if (!jsonSchemaModel.type?.includes('array')) {
return undefined;
}

const isNormalArray = !Array.isArray(jsonSchemaModel.items) && jsonSchemaModel.additionalItems === undefined && jsonSchemaModel.items !== undefined;
//item multiple types + additionalItems sat = both count, as normal array
//item single type + additionalItems sat = contradicting, only items count, as normal array
//item not sat + additionalItems sat = anything is allowed, as normal array
//item single type + additionalItems not sat = normal array
//item not sat + additionalItems not sat = normal array, any type
if (isNormalArray) {
const valueModel = convertToMetaModel(jsonSchemaModel.items as CommonModel);
return new ArrayModel(name, jsonSchemaModel.originalInput, valueModel);
}

const valueModel = new UnionModel('union', jsonSchemaModel.originalInput, []);
if (jsonSchemaModel.items !== undefined) {
for (const itemModel of Array.isArray(jsonSchemaModel.items) ? jsonSchemaModel.items : [jsonSchemaModel.items]) {
const itemsModel = convertToMetaModel(itemModel);
valueModel.union.push(itemsModel);
}
}
if (jsonSchemaModel.additionalItems !== undefined) {
const itemsModel = convertToMetaModel(jsonSchemaModel.additionalItems);
valueModel.union.push(itemsModel);
}
return new ArrayModel(name, jsonSchemaModel.originalInput, valueModel);
}
export function convertToTupleModel(jsonSchemaModel: CommonModel, name: string): TupleModel | undefined {
const isTuple = jsonSchemaModel.type?.includes('array') && Array.isArray(jsonSchemaModel.items) && jsonSchemaModel.additionalItems === undefined;
if (!isTuple) {
return undefined;
}

const items = jsonSchemaModel.items as CommonModel[];
//item multiple types + additionalItems not sat = tuple of item type
const tupleModel = new TupleModel(name, jsonSchemaModel.originalInput, []);
for (let i = 0; i < items.length; i++) {
const item = items[Number(i)];
const valueModel = convertToMetaModel(item);
const tupleValueModel = new TupleValueModel(i, valueModel);
tupleModel.tuple[Number(i)] = tupleValueModel;
}
return tupleModel;
}
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './FormatHelpers';
export * from './TypeHelpers';
export * from './NameHelpers';
export * from './FileHelpers';
export * from './CommonModelToMetaModel';
export * from './Splitter';
171 changes: 171 additions & 0 deletions test/helpers/CommonModelToMetaModel.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { ArrayModel, BooleanModel, CommonModel, FloatModel, IntegerModel, ObjectModel, StringModel, TupleModel, UnionModel } from '../../src';
import { convertToMetaModel } from '../../src/helpers';
describe('CommonModelToMetaModel', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should convert to string model', () => {
const cm = new CommonModel();
cm.type = 'string';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof StringModel).toEqual(true);
});
test('should convert to float model', () => {
const cm = new CommonModel();
cm.type = 'number';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof FloatModel).toEqual(true);
});
test('should convert to integer model', () => {
const cm = new CommonModel();
cm.type = 'integer';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof IntegerModel).toEqual(true);
});
test('should convert to boolean model', () => {
const cm = new CommonModel();
cm.type = 'boolean';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof BooleanModel).toEqual(true);
});
test('should convert to model', () => {
const cm = new CommonModel();
cm.type = 'object';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ObjectModel).toEqual(true);
});
test('should convert to array model', () => {
const cm = new CommonModel();
cm.type = 'array';
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ArrayModel).toEqual(true);
});
test('should convert to object model', () => {
const spm = new CommonModel();
spm.type = 'string';
const cm = new CommonModel();
cm.type = 'object';
cm.$id = 'test';
cm.properties = {
test: spm
};

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ObjectModel).toEqual(true);
});
test('should convert to object model with additional properties', () => {
const spm = new CommonModel();
spm.type = 'string';
const cm = new CommonModel();
cm.type = 'object';
cm.$id = 'test';
cm.properties = {
test: spm
};
cm.additionalProperties = spm;

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ObjectModel).toEqual(true);
expect((model as ObjectModel).properties['additionalProperties']).not.toBeUndefined();
});
test('should convert to object model with pattern properties', () => {
const spm = new CommonModel();
spm.type = 'string';
const cm = new CommonModel();
cm.type = 'object';
cm.$id = 'test';
cm.properties = {
test: spm
};
cm.patternProperties = {
test: spm
};

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ObjectModel).toEqual(true);
expect((model as ObjectModel).properties['test_PatternProperty']).not.toBeUndefined();
});
test('should convert normal array to array model', () => {
const spm = new CommonModel();
spm.type = 'string';
const cm = new CommonModel();
cm.type = 'array';
cm.$id = 'test';
cm.items = spm;

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ArrayModel).toEqual(true);
});
test('should convert array with additional items to array model as union type', () => {
const spm = new CommonModel();
spm.type = 'string';
const cm = new CommonModel();
cm.type = 'array';
cm.$id = 'test';
cm.items = spm;
cm.additionalItems = spm;

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof ArrayModel).toEqual(true);
expect((model as ArrayModel).valueModel instanceof UnionModel).toEqual(true);
});
test('should convert array of types to union model', () => {
const cm = new CommonModel();
cm.type = ['string', 'number'];
cm.$id = 'test';

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof UnionModel).toEqual(true);
expect((model as UnionModel).union.length).toEqual(2);
});
test('should convert tuple to tuple model', () => {
const scm = new CommonModel();
scm.type = 'string';
const cm = new CommonModel();
cm.type = 'array';
cm.$id = 'test';
cm.items = [scm, scm];

const model = convertToMetaModel(cm);

expect(model).not.toBeUndefined();
expect(model instanceof TupleModel).toEqual(true);
expect((model as TupleModel).tuple.length).toEqual(2);
});
});

0 comments on commit e34866b

Please sign in to comment.