Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
35 changes: 28 additions & 7 deletions lib/codegen/fromcto/golang/golangvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,31 +170,33 @@ class GoLangVisitor {
array = '[]';
}

// we export all fields by capitalizing them
// we strip $ as it is not legal in Go
const name = field.getName().startsWith('$') ? field.getName().substring(1) : field.getName();
const fieldName = ModelUtil.capitalizeFirstLetter(name);

if(ModelUtil.isMap(field)) {
const mapDeclaration = field.getModelFile().getType(field.getType());
let mapKeyType = mapDeclaration.getKey().getType();
let mapValueType = mapDeclaration.getValue().getType();

if (mapDeclaration.getModelFile().getType(mapKeyType).isScalarDeclaration?.()) {
if (mapDeclaration.getModelFile().getType(mapKeyType)?.isScalarDeclaration?.()) {
mapKeyType = mapDeclaration.getModelFile().getType(mapKeyType).getType();
}

const goKeyType = this.toGoType(mapKeyType);

if (mapDeclaration.getModelFile().getType(mapValueType).isScalarDeclaration?.()) {
if (mapDeclaration.getModelFile().getType(mapValueType)?.isScalarDeclaration?.()) {
mapValueType = mapDeclaration.getModelFile().getType(mapValueType).getType();
}

const goValueType = this.toGoType(mapValueType);

parameters.fileWriter.writeLine(1, `${field.getName()} := make(map[${goKeyType}]${goValueType})` + ' `json:"' + field.getName() + '"`');
parameters.fileWriter.writeLine(1, `${fieldName} map[${goKeyType}]${goValueType}` + ' `json:"' + field.getName() + '"`');
return null;
}

// we export all fields by capitalizing them
// we strip $ as it is not legal in Go
const name = field.getName().startsWith('$') ? field.getName().substring(1) : field.getName();
parameters.fileWriter.writeLine(1, ModelUtil.capitalizeFirstLetter(name) + ' ' + array + this.toGoType(field.getType()) + ' `json:"' + field.getName() + '"`' );
parameters.fileWriter.writeLine(1, fieldName + ' ' + array + this.toGoType(field.getType()) + ' `json:"' + field.getName() + '"`' );
return null;
}

Expand Down Expand Up @@ -264,6 +266,25 @@ class GoLangVisitor {
if(field.getType() === 'DateTime') {
return true;
}
if(typeof field.getParent === 'function' && field.getParent() === classDecl && ModelUtil.isMap(field)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit · conventions

The map key/value resolution (get the map declaration, follow scalar declarations to the underlying type) is now written out twice: here and in visitField. A small private helper that returns the resolved key and value types would keep the two copies from drifting apart. Optional.

const mapDeclaration = field.getModelFile().getType(field.getType());
if (mapDeclaration) {
let mapKeyType = mapDeclaration.getKey().getType();
let mapValueType = mapDeclaration.getValue().getType();

if (mapDeclaration.getModelFile().getType(mapKeyType)?.isScalarDeclaration?.()) {
mapKeyType = mapDeclaration.getModelFile().getType(mapKeyType).getType();
}

if (mapDeclaration.getModelFile().getType(mapValueType)?.isScalarDeclaration?.()) {
mapValueType = mapDeclaration.getModelFile().getType(mapValueType).getType();
}

if (mapKeyType === 'DateTime' || mapValueType === 'DateTime') {
return true;
}
}
}
}
}
return false;
Expand Down
12 changes: 6 additions & 6 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1059,11 +1059,11 @@ type Company struct {
concerto_1_0_0.Concept
Name string \`json:"name"\`
Headquarters Address \`json:"headquarters"\`
companyProperties := make(map[string]string) \`json:"companyProperties"\`
employeeDirectory := make(map[string]Employee) \`json:"employeeDirectory"\`
employeeTShirtSizes := make(map[string]TShirtSizeType) \`json:"employeeTShirtSizes"\`
employeeProfiles := make(map[string]Concept) \`json:"employeeProfiles"\`
employeeSocialSecurityNumbers := make(map[string]string) \`json:"employeeSocialSecurityNumbers"\`
CompanyProperties map[string]string \`json:"companyProperties"\`
EmployeeDirectory map[string]Employee \`json:"employeeDirectory"\`
EmployeeTShirtSizes map[string]TShirtSizeType \`json:"employeeTShirtSizes"\`
EmployeeProfiles map[string]Concept \`json:"employeeProfiles"\`
EmployeeSocialSecurityNumbers map[string]string \`json:"employeeSocialSecurityNumbers"\`
}
type Department int
const (
Expand Down Expand Up @@ -1097,7 +1097,7 @@ type Person struct {
Ssn string \`json:"ssn"\`
Height float64 \`json:"height"\`
Dob time.Time \`json:"dob"\`
nextOfKin := make(map[string]string) \`json:"nextOfKin"\`
NextOfKin map[string]string \`json:"nextOfKin"\`
}
type Employee struct {
Person
Expand Down
121 changes: 120 additions & 1 deletion test/codegen/fromcto/golang/golangvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,39 @@ describe('GoLangVisitor', function () {

goVisit.visitField(mockField,param);

param.fileWriter.writeLine.withArgs(1, 'dictionary := make(map[string]string) `json:"dictionary"`').calledOnce.should.be.ok;
param.fileWriter.writeLine.withArgs(1, 'Dictionary map[string]string `json:"dictionary"`').calledOnce.should.be.ok;
Comment thread
chaitijain593-prog marked this conversation as resolved.
Resolved
});

it('should handle map field when getType returns null for external or primitive types', () => {
let param = {
fileWriter: mockFileWriter,
};

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

const mockField = sinon.createStubInstance(Field);
const mockMapDeclaration = sinon.createStubInstance(MapDeclaration);

mockField.getType.returns('Map1');
mockField.getName.returns('properties');
mockField.getModelFile.returns({
getType: sinon.stub().returns(mockMapDeclaration),
});

mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.getKey.returns({ getType: () => 'String' });
mockMapDeclaration.getValue.returns({ getType: () => 'ExternalType' });
// getType returns null (e.g. cross-namespace or unresolved type)
mockMapDeclaration.getModelFile.returns({
getType: sinon.stub().returns(null),
});

goVisit.visitField(mockField, param);

param.fileWriter.writeLine.withArgs(1, 'Properties map[string]ExternalType `json:"properties"`').calledOnce.should.be.ok;
});

});
Expand Down Expand Up @@ -477,6 +509,93 @@ describe('GoLangVisitor', function () {

goVisit.containsDateTimeField(mockModelFileDefinition).should.deep.equal(false);
});

it('should return true if the model file contains a map field with DateTime value', () => {
const mm = new ModelManager();
const modelFile = mm.addCTOModel(`
namespace org.acme@1.0.0
map TimestampMap {
o String
o DateTime
}
concept EventLog {
o TimestampMap timestamps
}
`);
goVisit.containsDateTimeField(modelFile).should.be.true;
});

it('should return true if the model file contains a map field with a scalar extending DateTime', () => {
const mm = new ModelManager();
const modelFile = mm.addCTOModel(`
namespace org.acme@1.0.0
scalar EventTime extends DateTime
map EventMap {
o String
o EventTime
}
concept LogEntry {
o EventMap events
}
`);
goVisit.containsDateTimeField(modelFile).should.be.true;
});

it('should return false if the model file contains a map field without DateTime', () => {
const mm = new ModelManager();
const modelFile = mm.addCTOModel(`
namespace org.acme@1.0.0
map StringMap {
o String
o String
}
concept LogEntry {
o StringMap items
}
`);
goVisit.containsDateTimeField(modelFile).should.be.false;
});

it('should write import "time" when the only DateTime in the model is in a map field', () => {
const mm = new ModelManager();
const modelFile = mm.addCTOModel(`
namespace org.acme@1.0.0
map TimestampMap {
o String
o DateTime
}
concept EventLog {
o TimestampMap timestamps
}
`);
let param = {
fileWriter: mockFileWriter,
};
goVisit.visitModelFile(modelFile, param);
param.fileWriter.writeLine.withArgs(0, 'import "time"').calledOnce.should.be.ok;
});

it('should return false for child class inheriting DateTime map from another namespace', () => {
const mm = new ModelManager();
mm.addCTOModel(`
namespace base@1.0.0
map TimestampMap {
o String
o DateTime
}
concept Parent {
o TimestampMap timestamps
}
`);
const childModelFile = mm.addCTOModel(`
namespace child@1.0.0
import base@1.0.0.Parent
concept Child extends Parent {
o String name
}
`);
goVisit.containsDateTimeField(childModelFile).should.be.false;
});
});

describe('toGoType', () => {
Expand Down
Loading