Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add CommonModel conversion to MetaModel #677

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
});