Skip to content

Commit

Permalink
feat(map): Extend supported Key Types for Typescript codegen (#70)
Browse files Browse the repository at this point in the history
* feat(*): keys are variable typed

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

* test(*): update test

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

* test(*): update snapshot

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

* test(*): adds scalar test coverage

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

* feat(*): minor refactor, update tests

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

* test(*): update snapshot

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

---------

Signed-off-by: Jonathan Casey <[email protected]>
  • Loading branch information
jonathan-casey authored Nov 13, 2023
1 parent 79653dd commit 01bb536
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 34 deletions.
35 changes: 22 additions & 13 deletions lib/codegen/fromcto/typescript/typescriptvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,24 +281,33 @@ class TypescriptVisitor {
* @private
*/
visitMapDeclaration(mapDeclaration, parameters) {
// const keyType = mapDeclaration.getKey().getType(); // for now, key is always string.
let valueType = mapDeclaration.getValue().getType();

const isScalar = ModelUtil.isScalar(mapDeclaration.getValue());
const isPrimitive = ModelUtil.isPrimitiveType(mapDeclaration.getValue().getType());
const mapKeyType = mapDeclaration.getKey().getType();
const mapValueType = mapDeclaration.getValue().getType();

let keyType;
let valueType;

// Map Key Type
if (ModelUtil.isPrimitiveType(mapKeyType)) {
keyType = this.toTsType(mapDeclaration.getKey().getType(), false, false);
} else if (ModelUtil.isScalar(mapDeclaration.getKey())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getKey().getType());
const scalarType = scalarDeclaration.getType();
keyType = this.toTsType(scalarType, false, false);
}

if (isScalar) {
const scalar = mapDeclaration.getModelFile(mapDeclaration.getValue().getType());
const scalarType = scalar.getType();
valueType = this.toTsType(scalarType, false, false);
} else if (isPrimitive) {
// Map Value Type
if (ModelUtil.isPrimitiveType(mapValueType)) {
valueType = this.toTsType(mapDeclaration.getValue().getType(), false, false);
} else if (ModelUtil.isScalar(mapDeclaration.getValue())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getValue().getType());
const scalarType = scalarDeclaration.getType();
valueType = this.toTsType(scalarType, false, false);
} else {
valueType = this.toTsType(valueType, true, false);

valueType = this.toTsType(mapValueType, true, false);
}

parameters.fileWriter.writeLine(0, 'export type ' + mapDeclaration.getName() + ` = Map<string, ${valueType}>;\n` );
parameters.fileWriter.writeLine(0, 'export type ' + mapDeclaration.getName() + ` = Map<${keyType}, ${valueType}>;\n` );

return null;
}
Expand Down
20 changes: 8 additions & 12 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ type Person struct {
Ssn string \`json:"ssn"\`
Height float64 \`json:"height"\`
Dob time.Time \`json:"dob"\`
nextOfKin := make(map[string]time.Time) \`json:"nextOfKin"\`
nextOfKin := make(map[string]string) \`json:"nextOfKin"\`
}
type Employee struct {
Person
Expand Down Expand Up @@ -2141,7 +2141,6 @@ exports[`codegen #formats check we can convert all formats from namespace unvers
"type": "string"
},
"org.acme.hr.KinTelephone": {
"format": "date-time",
"type": "string"
}
}
Expand Down Expand Up @@ -3260,7 +3259,6 @@ exports[`codegen #formats check we can convert all formats from namespace unvers
"type": "string"
},
"org.acme.hr.KinTelephone": {
"format": "date-time",
"type": "string"
}
}
Expand Down Expand Up @@ -4764,11 +4762,11 @@ import {IConcept,IAsset,IParticipant,IEvent,ITransaction} from './[email protected]
// interfaces
export type CompanyProperties = Map<string, string>;

export type EmployeeLoginTimes = Map<string, null>;
export type EmployeeLoginTimes = Map<string, Date>;

export type EmployeeSocialSecurityNumbers = Map<string, null>;
export type EmployeeSocialSecurityNumbers = Map<string, string>;

export type NextOfKin = Map<string, null>;
export type NextOfKin = Map<string, string>;

export type EmployeeProfiles = Map<string, IConcept>;

Expand Down Expand Up @@ -6180,7 +6178,7 @@ type Person struct {
Ssn string \`json:"ssn"\`
Height float64 \`json:"height"\`
Dob time.Time \`json:"dob"\`
nextOfKin := make(map[string]time.Time) \`json:"nextOfKin"\`
nextOfKin := make(map[string]string) \`json:"nextOfKin"\`
}
type Employee struct {
Person
Expand Down Expand Up @@ -7693,7 +7691,6 @@ exports[`codegen #formats check we can convert all formats from namespace versio
"type": "string"
},
"[email protected]": {
"format": "date-time",
"type": "string"
}
}
Expand Down Expand Up @@ -8828,7 +8825,6 @@ exports[`codegen #formats check we can convert all formats from namespace versio
"type": "string"
},
"[email protected]": {
"format": "date-time",
"type": "string"
}
}
Expand Down Expand Up @@ -10348,11 +10344,11 @@ import {IConcept,IAsset,IParticipant,IEvent,ITransaction} from './[email protected]
// interfaces
export type CompanyProperties = Map<string, string>;

export type EmployeeLoginTimes = Map<string, null>;
export type EmployeeLoginTimes = Map<string, Date>;

export type EmployeeSocialSecurityNumbers = Map<string, null>;
export type EmployeeSocialSecurityNumbers = Map<string, string>;

export type NextOfKin = Map<string, null>;
export type NextOfKin = Map<string, string>;

export type EmployeeProfiles = Map<string, IConcept>;

Expand Down
2 changes: 1 addition & 1 deletion test/codegen/fromcto/data/model/hr.cto
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ transaction ChangeOfAddress {
}

scalar KinName extends String
scalar KinTelephone extends DateTime
scalar KinTelephone extends String
128 changes: 120 additions & 8 deletions test/codegen/fromcto/typescript/typescriptvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ chai.should();
const sinon = require('sinon');

const TypescriptVisitor = require('../../../../lib/codegen/fromcto/typescript/typescriptvisitor.js');
const { MapDeclaration, ModelUtil } = require('@accordproject/concerto-core');
const { MapDeclaration, MapKeyType, ModelUtil, ScalarDeclaration } = require('@accordproject/concerto-core');

const ClassDeclaration = require('@accordproject/concerto-core').ClassDeclaration;
const EnumDeclaration = require('@accordproject/concerto-core').EnumDeclaration;
Expand All @@ -29,6 +29,7 @@ const ModelFile = require('@accordproject/concerto-core').ModelFile;
const ModelManager = require('@accordproject/concerto-core').ModelManager;
const RelationshipDeclaration = require('@accordproject/concerto-core').RelationshipDeclaration;
const FileWriter = require('@accordproject/concerto-util').FileWriter;
let sandbox = sinon.createSandbox();

describe('TypescriptVisitor', function () {
let typescriptVisitor;
Expand Down Expand Up @@ -642,20 +643,27 @@ describe('TypescriptVisitor', function () {
});

describe('visitMapValueDeclaration', () => {

before(() => {
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return false;
});
});

it('should write a line with the name, key and value of the map <String, String>', () => {
let param = {
fileWriter: mockFileWriter
};

sandbox.stub(ModelUtil, 'isPrimitiveType').callsFake(() => {
return true;
});

let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);

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

sinon.stub(ModelUtil, 'isScalar').callsFake(() => {
return false;
});

getKeyType.returns('String');
getValueType.returns('String');
mockMapDeclaration.getName.returns('Map1');
Expand Down Expand Up @@ -695,16 +703,22 @@ describe('TypescriptVisitor', function () {
fileWriter: mockFileWriter
};

sandbox.restore();
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return false;
});
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let mockMapKeyType = sinon.createStubInstance(MapKeyType);

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

mockMapKeyType.getType.returns('String');
getKeyType.returns('String');
getValueType.returns('Address');
mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.isMapDeclaration.returns(true);
mockMapDeclaration.getKey.returns({ getType: getKeyType });
mockMapDeclaration.getKey.returns(mockMapKeyType);
mockMapDeclaration.getValue.returns({ getType: getValueType });

typescriptVisitor.visitMapDeclaration(mockMapDeclaration, param);
Expand All @@ -717,21 +731,119 @@ describe('TypescriptVisitor', function () {
fileWriter: mockFileWriter
};

sandbox.restore();
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return false;
});
let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let mockMapKeyType = sinon.createStubInstance(MapKeyType);

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

getKeyType.returns('String');
mockMapKeyType.getType.returns('DateTime');
getKeyType.returns('DateTime');
getValueType.returns('Address');
mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.isMapDeclaration.returns(true);
mockMapDeclaration.getKey.returns(mockMapKeyType);
mockMapDeclaration.getValue.returns({ getType: getValueType });

typescriptVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(0, 'export type Map1 = Map<Date, IAddress>;\n').calledOnce.should.be.ok;
});

it('should write a line with the name, key and value of the map <String, Concept>', () => {
let param = {
fileWriter: mockFileWriter
};
sandbox.restore();
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return false;
});

let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);

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

getKeyType.returns('String');
getValueType.returns('Concept');
mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.isMapDeclaration.returns(true);
mockMapDeclaration.getKey.returns({ getType: getKeyType });
mockMapDeclaration.getValue.returns({ getType: getValueType });

typescriptVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(0, 'export type Map1 = Map<string, IAddress>;\n').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(0, 'export type Map1 = Map<string, IConcept>;\n').calledOnce.should.be.ok;
});

it('should write a line with the name, key and value of the map <SSN, String>', () => {
let param = {
fileWriter: mockFileWriter
};

sandbox.restore();
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return true;
});

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

mockModelFile.getType.returns(ScalarDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();

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

typescriptVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(0, 'export type Map1 = Map<string, string>;\n').calledOnce.should.be.ok;
});

it('should write a line with the name, key and value of the map <String, SSN>', () => {
let param = {
fileWriter: mockFileWriter
};

sandbox.restore();
sandbox.stub(ModelUtil, 'isScalar').callsFake(() => {
return true;
});

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

mockModelFile.getType.returns(ScalarDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();

mockModelFile.getType.returns(mockScalarDeclaration);
mockScalarDeclaration.getType.returns('String');
getKeyType.returns('string');
getValueType.returns('SSN');
mockMapDeclaration.getModelFile.returns(mockModelFile);
mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.isMapDeclaration.returns(true);
mockMapDeclaration.getKey.returns({ getType: getKeyType });
mockMapDeclaration.getValue.returns({ getType: getValueType });

typescriptVisitor.visitMapDeclaration(mockMapDeclaration, param);

param.fileWriter.writeLine.withArgs(0, 'export type Map1 = Map<string, string>;\n').calledOnce.should.be.ok;
});
});

Expand Down

0 comments on commit 01bb536

Please sign in to comment.