Skip to content

Commit

Permalink
Merge 162ebf6 into 2e2a70b
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored May 20, 2021
2 parents 2e2a70b + 162ebf6 commit c2f39d4
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/interpretation_of_JSON_Schema_draft_7.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The order of transformation:
- [allOf](#allOf-sub-schemas)
- `const` overwrite already interpreted `enums`.
- `items` are determined as is, where more then 1 item are merged.
- `additionalProperties` are determined as is, where duplicate additionalProperties for the model are merged together. If the schema does not define additionalProperties it defaults to `true` schema.
- [oneOf/anyOf/then/else](#Processing-sub-schemas)

## allOf sub schemas
Expand Down
19 changes: 19 additions & 0 deletions src/interpreter/InterpretAdditionalProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CommonModel } from 'models';
import { Schema } from 'models/Schema';
import { Interpreter } from './Interpreter';
import { isModelObject } from './Utils';

/**
* Interpreter function for JSON Schema draft 7 additionalProperties keyword.
*
* @param schema
* @param model
* @param interpreter
*/
export default function interpretAdditionalProperties(schema: Schema | boolean, model: CommonModel, interpreter : Interpreter) {
if (typeof schema === 'boolean' || !isModelObject(model)) return;
const newModels = interpreter.interpret(schema.additionalProperties || true);
if (newModels.length > 0) {
model.addAdditionalProperty(newModels[0], schema);
}
}
4 changes: 3 additions & 1 deletion src/interpreter/Interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { interpretName, isModelObject } from './Utils';
import interpretProperties from './InterpretProperties';
import interpretAllOf from './InterpretAllOf';
import interpretConst from './InterpretConst';
import { Logger } from '../utils';
import interpretAdditionalProperties from './InterpretAdditionalProperties';
import interpretItems from './InterpretItems';
import { Logger } from '../utils';

export class Interpreter {
static defaultOptions: SimplificationOptions = {
Expand Down Expand Up @@ -80,6 +81,7 @@ export class Interpreter {

model.required = schema.required || model.required;

interpretAdditionalProperties(schema, model, this);
interpretItems(schema, model, this);
interpretProperties(schema, model, this);
interpretAllOf(schema, model, this);
Expand Down
16 changes: 16 additions & 0 deletions src/models/CommonModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ export class CommonModel extends CommonSchema<CommonModel> {
}
}

/**
* Adds additionalProperty to the model.
* If another model already are added the two are merged.
*
* @param additionalPropertiesModel
* @param schema
*/
addAdditionalProperty(additionalPropertiesModel: CommonModel, schema: Schema) {
if (this.additionalProperties !== undefined) {
Logger.warn('While trying to add additionalProperties to model, but it is already present, merging models together', additionalPropertiesModel, schema, this);
this.additionalProperties = CommonModel.mergeCommonModels(this.additionalProperties, additionalPropertiesModel, schema);
} else {
this.additionalProperties = additionalPropertiesModel;
}
}

/**
* Adds another model this model should extend.
*
Expand Down
8 changes: 8 additions & 0 deletions test/interpreter/Intepreter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import interpretProperties from '../../src/interpreter/InterpretProperties';
import interpretConst from '../../src/interpreter/InterpretConst';
import interpretAllOf from '../../src/interpreter/InterpretAllOf';
import interpretItems from '../../src/interpreter/InterpretItems';
import interpretAdditionalProperties from '../../src/interpreter/InterpretAdditionalProperties';
import { CommonModel, Schema } from '../../src/models';

let mockedIsModelObjectReturn = false;
Expand All @@ -19,6 +20,7 @@ jest.mock('../../src/interpreter/InterpretProperties');
jest.mock('../../src/interpreter/InterpretConst');
jest.mock('../../src/interpreter/InterpretAllOf');
jest.mock('../../src/interpreter/InterpretItems');
jest.mock('../../src/interpreter/InterpretAdditionalProperties');
CommonModel.mergeCommonModels = jest.fn();
/**
* Some of these test are purely theoretical and have little if any merit
Expand Down Expand Up @@ -184,6 +186,12 @@ describe('Interpreter', function() {
interpreter.interpret(schema);
expect(interpretItems).toHaveBeenNthCalledWith(1, schema, expect.anything(), expect.anything());
});
test('should always try to interpret additionalProperties', function() {
const schema = {};
const interpreter = new Interpreter();
interpreter.interpret(schema);
expect(interpretAdditionalProperties).toHaveBeenNthCalledWith(1, schema, expect.anything(), expect.anything());
});

test('should support primitive roots', function() {
const schema = { 'type': 'string' };
Expand Down
61 changes: 61 additions & 0 deletions test/interpreter/InterpretAdditionalProperties.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable no-undef */
import { CommonModel } from '../../src/models/CommonModel';
import { Interpreter } from '../../src/interpreter/Interpreter';
import interpretAdditionalProperties from '../../src/interpreter/InterpretAdditionalProperties';

let mockedReturnModels = [new CommonModel()];
jest.mock('../../src/interpreter/Interpreter', () => {
return {
Interpreter: jest.fn().mockImplementation(() => {
return {
interpret: jest.fn().mockReturnValue(mockedReturnModels)
};
})
};
});
jest.mock('../../src/models/CommonModel');
describe('Interpretation of additionalProperties', () => {
beforeEach(() => {
jest.clearAllMocks();
mockedReturnModels = [new CommonModel()];
});
afterAll(() => {
jest.restoreAllMocks();
});
test('should try and interpret additionalProperties schema', () => {
const schema: any = { additionalProperties: { type: 'string' } };
const model = new CommonModel();
model.type = "object";
const interpreter = new Interpreter();
interpretAdditionalProperties(schema, model, interpreter);
expect(interpreter.interpret).toHaveBeenNthCalledWith(1, { type: 'string' });
expect(model.addAdditionalProperty).toHaveBeenNthCalledWith(1, mockedReturnModels[0], schema);
});
test('should ignore model if interpreter cannot interpret additionalProperty schema', () => {
const schema: any = { };
const model = new CommonModel();
model.type = "object";
const interpreter = new Interpreter();
mockedReturnModels.pop();
interpretAdditionalProperties(schema, model, interpreter);
expect(model.addAdditionalProperty).not.toHaveBeenCalled();
});
test('should only work if model is object type', () => {
const schema: any = { };
const model = new CommonModel();
model.type = "string";
const interpreter = new Interpreter();
interpretAdditionalProperties(schema, model, interpreter);
expect(interpreter.interpret).not.toHaveBeenCalled();
expect(model.addAdditionalProperty).not.toHaveBeenCalled();
});
test('should default to true', () => {
const schema: any = { };
const model = new CommonModel();
model.type = "object";
const interpreter = new Interpreter();
interpretAdditionalProperties(schema, model, interpreter);
expect(interpreter.interpret).toHaveBeenNthCalledWith(1, true);
expect(model.addAdditionalProperty).toHaveBeenNthCalledWith(1, mockedReturnModels[0], schema);
});
});
26 changes: 26 additions & 0 deletions test/models/CommonModel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,32 @@ describe('CommonModel', function() {
});
});


describe('addAdditionalProperty', function() {
beforeAll(() => {
jest.spyOn(CommonModel, "mergeCommonModels");
});
afterEach(() => {
jest.clearAllMocks();
});
test('should add additionalProperties to model', function() {
const additionalPropertiesModel = new CommonModel();
additionalPropertiesModel.$id = "test";
const model = new CommonModel();
model.addAdditionalProperty(additionalPropertiesModel, {});
expect(model.additionalProperties).toEqual(additionalPropertiesModel);
expect(CommonModel.mergeCommonModels).not.toHaveBeenCalled();
});
test('should merge additionalProperties together', function() {
const additionalPropertiesModel = new CommonModel();
additionalPropertiesModel.$id = "test";
const model = new CommonModel();
model.addAdditionalProperty(additionalPropertiesModel, {});
model.addAdditionalProperty(additionalPropertiesModel, {});
expect(model.additionalProperties).toEqual(additionalPropertiesModel);
expect(CommonModel.mergeCommonModels).toHaveBeenNthCalledWith(1, additionalPropertiesModel, additionalPropertiesModel, {});
});
});
describe('addTypes', function() {
test('should add multiple types', function() {
const model = new CommonModel();
Expand Down

0 comments on commit c2f39d4

Please sign in to comment.