Skip to content

Commit

Permalink
feat(map): Codegen for OData Target (#76)
Browse files Browse the repository at this point in the history
* feat(*): extend odata visitor

Signed-off-by: Jonathan Casey <[email protected]>

* test(*): update snapshot

Signed-off-by: Jonathan Casey <[email protected]>

* test(*): add test cov

Signed-off-by: Jonathan Casey <[email protected]>

* test(*): fix tests

Signed-off-by: Jonathan Casey <[email protected]>

* feat(*): returns on visitMapDeclaration

Signed-off-by: Jonathan Casey <[email protected]>

---------

Signed-off-by: Jonathan Casey <[email protected]>
  • Loading branch information
jonathan-casey authored Nov 22, 2023
1 parent 717194d commit e38396e
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 1 deletion.
39 changes: 38 additions & 1 deletion lib/codegen/fromcto/odata/odatavisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class ODataVisitor {
} else if (thing.isClassDeclaration?.()) {
return this.visitClassDeclaration(thing, parameters);
} else if (thing.isMapDeclaration?.()) {
return;
return this.visitMapDeclaration(thing, parameters);
} else if (thing.isTypeScalar?.()) {
return this.visitField(thing.getScalarField(), parameters);
} else if (thing.isField?.()) {
Expand Down Expand Up @@ -280,6 +280,43 @@ class ODataVisitor {
return null;
}

/**
* Visitor design pattern
* @param {MapDeclaration} mapDeclaration - the object being visited
* @param {Object} parameters - the parameter
* @return {Object} the result of visiting or null
* @private
*/
visitMapDeclaration(mapDeclaration, parameters) {
let keyType = mapDeclaration.getKey().getType();
let valueType = mapDeclaration.getValue().getType();

if (ModelUtil.isPrimitiveType(keyType)) {
keyType = this.toODataType(keyType);
} else if (ModelUtil.isScalar(mapDeclaration.getKey())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getKey().getType());
const scalarType = scalarDeclaration.getType();
keyType = this.toODataType(scalarType);
}

if (ModelUtil.isPrimitiveType(valueType)) {
valueType = this.toODataType(valueType);
} else if (ModelUtil.isScalar(mapDeclaration.getValue())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getValue().getType());
const scalarType = scalarDeclaration.getType();
valueType = this.toODataType(scalarType);
} else {
valueType = ModelUtil.removeNamespaceVersionFromFullyQualifiedName(mapDeclaration.getModelFile().getFullyQualifiedTypeName(valueType));
}

parameters.fileWriter.writeLine(1, `<ComplexType Name="${mapDeclaration.getName()}">`);
parameters.fileWriter.writeLine(2, `<Property Name="Key" Type="${keyType}"/>`);
parameters.fileWriter.writeLine(2, `<Property Name="Value" Type="${valueType}"/>`);
parameters.fileWriter.writeLine(1, '</ComplexType>');

return null;
}

/**
* Visitor design pattern
* @param {Relationship} relationship - the object being visited
Expand Down
56 changes: 56 additions & 0 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2686,6 +2686,10 @@ exports[`codegen #formats check we can convert all formats from namespace unvers
<Member Name="LARGE">
</Member>
</EnumType>
<ComplexType Name="EmployeeTShirtSizes">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="org.acme.hr.base.TShirtSizeType"/>
</ComplexType>
<ComplexType Name="Address" BaseType="concerto.Concept">
<Property Name="street" Type="Edm.String" >
</Property>
Expand Down Expand Up @@ -2723,6 +2727,30 @@ exports[`codegen #formats check we can convert all formats from namespace unvers
</edmx:Reference>
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="org.acme.hr">
<ComplexType Name="CompanyProperties">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="EmployeeLoginTimes">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.DateTimeOffset"/>
</ComplexType>
<ComplexType Name="EmployeeSocialSecurityNumbers">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="NextOfKin">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="EmployeeProfiles">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="concerto.Concept"/>
</ComplexType>
<ComplexType Name="EmployeeDirectory">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="org.acme.hr.Employee"/>
</ComplexType>
<ComplexType Name="Company" BaseType="concerto.Concept">
<Property Name="name" Type="Edm.String" >
</Property>
Expand Down Expand Up @@ -8660,6 +8688,10 @@ exports[`codegen #formats check we can convert all formats from namespace versio
<Member Name="LARGE">
</Member>
</EnumType>
<ComplexType Name="EmployeeTShirtSizes">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="org.acme.hr.base.TShirtSizeType"/>
</ComplexType>
<ComplexType Name="Address" BaseType="concerto.Concept">
<Property Name="street" Type="Edm.String" >
</Property>
Expand Down Expand Up @@ -8697,6 +8729,30 @@ exports[`codegen #formats check we can convert all formats from namespace versio
</edmx:Reference>
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="org.acme.hr">
<ComplexType Name="CompanyProperties">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="EmployeeLoginTimes">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.DateTimeOffset"/>
</ComplexType>
<ComplexType Name="EmployeeSocialSecurityNumbers">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="NextOfKin">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="Edm.String"/>
</ComplexType>
<ComplexType Name="EmployeeProfiles">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="concerto.Concept"/>
</ComplexType>
<ComplexType Name="EmployeeDirectory">
<Property Name="Key" Type="Edm.String"/>
<Property Name="Value" Type="org.acme.hr.Employee"/>
</ComplexType>
<ComplexType Name="Company" BaseType="concerto.Concept">
<Property Name="name" Type="Edm.String" >
</Property>
Expand Down
164 changes: 164 additions & 0 deletions test/codegen/fromcto/odata/odatavisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ chai.should();
const sinon = require('sinon');

const ODataVisitor = require('../../../../lib/codegen/fromcto/odata/odatavisitor.js');
const { ConceptDeclaration } = require('@accordproject/concerto-core');

const ClassDeclaration = require('@accordproject/concerto-core').ClassDeclaration;
const ScalarDeclaration = require('@accordproject/concerto-core').ScalarDeclaration;
const EnumDeclaration = require('@accordproject/concerto-core').EnumDeclaration;
const MapDeclaration = require('@accordproject/concerto-core').MapDeclaration;
const ModelUtil = require('@accordproject/concerto-core').ModelUtil;
const EnumValueDeclaration = require('@accordproject/concerto-core').EnumValueDeclaration;
const Field = require('@accordproject/concerto-core').Field;
const ModelFile = require('@accordproject/concerto-core').ModelFile;
Expand All @@ -30,6 +34,8 @@ const RelationshipDeclaration = require('@accordproject/concerto-core').Relation
const Decorator = require('@accordproject/concerto-core').Decorator;
const FileWriter = require('@accordproject/concerto-util').FileWriter;

let sandbox = sinon.createSandbox();

describe('ODataVisitor', function () {
let oDataVisitor;
let mockFileWriter;
Expand Down Expand Up @@ -326,6 +332,164 @@ describe('ODataVisitor', function () {
});
});

describe('visitMapDeclaration', () => {
it('should write the map <String, String>', () => {
let param = {
fileWriter: mockFileWriter
};

let mockModelFile = sinon.createStubInstance(ModelFile);
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);

const getKeyType = sinon.stub();
const getValueType = sinon.stub();

getKeyType.returns('String');
getValueType.returns('String');

mockModelFile.getType.returns(mockMapDeclaration);
mockMapDeclaration.getName.returns('MockMap');
mockMapDeclaration.getKey.returns({getType: getKeyType});
mockMapDeclaration.getValue.returns({getType: getValueType});

const isPrimitiveTypeStub = sandbox.stub(ModelUtil, 'isPrimitiveType');

isPrimitiveTypeStub.onCall(0).returns(true);
isPrimitiveTypeStub.onCall(1).returns(true);

oDataVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(1, '<ComplexType Name="MockMap">').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Key" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Value" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(1, '</ComplexType>').calledOnce.should.be.ok;
isPrimitiveTypeStub.restore();
});

it('should write the map <Scalar, String>', () => {
let param = {
fileWriter: mockFileWriter
};

let mockModelFile = sinon.createStubInstance(ModelFile);
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let mockScalarDeclaration = sinon.createStubInstance(ScalarDeclaration);


const getKeyType = sinon.stub();
const getValueType = sinon.stub();

getKeyType.returns('SSN');
getValueType.returns('String');

mockMapDeclaration.getName.returns('MockMap');
mockMapDeclaration.getKey.returns({getType: getKeyType});
mockMapDeclaration.getValue.returns({getType: getValueType});
mockMapDeclaration.getModelFile.returns(mockModelFile);
mockModelFile.getType.returns(mockScalarDeclaration);
mockScalarDeclaration.getType.returns('String');

const isPrimitiveTypeStub = sandbox.stub(ModelUtil, 'isPrimitiveType');
const isScalarStub = sandbox.stub(ModelUtil, 'isScalar');

isPrimitiveTypeStub.onCall(0).returns(false);
isPrimitiveTypeStub.onCall(1).returns(true);

isScalarStub.onCall(0).returns(true);
isScalarStub.onCall(1).returns(false);

oDataVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(1, '<ComplexType Name="MockMap">').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Key" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Value" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(1, '</ComplexType>').calledOnce.should.be.ok;
isPrimitiveTypeStub.restore();
isScalarStub.restore();
});

it('should write the map <Scalar, Scalar>', () => {
let param = {
fileWriter: mockFileWriter
};

let mockModelFile = sinon.createStubInstance(ModelFile);
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let mockScalarDeclaration = sinon.createStubInstance(ScalarDeclaration);

const getKeyType = sinon.stub();
const getValueType = sinon.stub();

getKeyType.returns('SSN');
getValueType.returns('SSN');

mockMapDeclaration.getName.returns('MockMap');
mockMapDeclaration.getKey.returns({getType: getKeyType});
mockMapDeclaration.getValue.returns({getType: getValueType});
mockMapDeclaration.getModelFile.returns(mockModelFile);
mockModelFile.getType.returns(mockScalarDeclaration);
mockScalarDeclaration.getType.returns('String');

const isPrimitiveTypeStub = sandbox.stub(ModelUtil, 'isPrimitiveType');
const isScalarStub = sandbox.stub(ModelUtil, 'isScalar');

isPrimitiveTypeStub.onCall(0).returns(false);
isPrimitiveTypeStub.onCall(1).returns(false);

isScalarStub.onCall(0).returns(true);
isScalarStub.onCall(1).returns(true);

oDataVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(1, '<ComplexType Name="MockMap">').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Key" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Value" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(1, '</ComplexType>').calledOnce.should.be.ok;
isPrimitiveTypeStub.restore();
isScalarStub.restore();
});

it('should write the map <String, Concept>', () => {
let param = {
fileWriter: mockFileWriter
};

let mockModelFile = sinon.createStubInstance(ModelFile);
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let mockConceptDeclaration = sinon.createStubInstance(ConceptDeclaration);

const getKeyType = sinon.stub();
const getValueType = sinon.stub();

getKeyType.returns('String');
getValueType.returns('Person');

mockMapDeclaration.getName.returns('MockMap');
mockMapDeclaration.getKey.returns({getType: getKeyType});
mockMapDeclaration.getValue.returns({getType: getValueType});
mockMapDeclaration.getModelFile.returns(mockModelFile);
mockModelFile.getType.returns(mockConceptDeclaration);
mockModelFile.getFullyQualifiedTypeName.returns('[email protected]');

const isPrimitiveTypeStub = sandbox.stub(ModelUtil, 'isPrimitiveType');
const isScalarStub = sandbox.stub(ModelUtil, 'isScalar');

isPrimitiveTypeStub.onCall(0).returns(true);
isPrimitiveTypeStub.onCall(1).returns(false);

isScalarStub.onCall(0).returns(false);

oDataVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(1, '<ComplexType Name="MockMap">').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Key" Type="Edm.String"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(2, '<Property Name="Value" Type="com.acme.Person"/>').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(1, '</ComplexType>').calledOnce.should.be.ok;
isPrimitiveTypeStub.restore();
isScalarStub.restore();
});
});

describe('visitClassDeclaration', () => {
let param;
beforeEach(() => {
Expand Down

0 comments on commit e38396e

Please sign in to comment.