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

feat(map): Extend supported Key Types for C# codegen #71

Merged
merged 10 commits into from
Jan 25, 2024
38 changes: 34 additions & 4 deletions lib/codegen/fromcto/csharp/csharpvisitor.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-unreachable */
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -312,11 +313,40 @@ class CSharpVisitor {
* @private
*/
writeField(field, parameters, externalFieldType, isOptional = false) {

// write Map field
if (Object.keys(field).length > 0 && ModelUtil.isMap?.(field)) {
const decl = field.getModelFile().getType(field.getType());
parameters.fileWriter.writeLine(1, `public Dictionary<string, ${this.toCSharpType(decl.getValue().getType())}> ${field.getName()} { get; set; }`);
if (ModelUtil.isMap?.(field)) {
const mapDeclaration = field.getModelFile().getType(field.getType());
const mapKeyType = mapDeclaration.getKey().getType();
const mapValueType = mapDeclaration.getValue().getType();

let keyType;
let valueType;

// Map Key
if (ModelUtil.isPrimitiveType(mapKeyType)) {
keyType = this.toCSharpType(mapKeyType);
}
else if (ModelUtil.isScalar(mapDeclaration.getKey())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getKey().getType());
const keyFQN = ModelUtil.removeNamespaceVersionFromFullyQualifiedName(scalarDeclaration.getFullyQualifiedName());
const scalarType = keyFQN === 'concerto.scalar.UUID' ? keyFQN : scalarDeclaration.getType();
keyType = this.toCSharpType(scalarType);
}

// Map Value
if (ModelUtil.isPrimitiveType(mapValueType)) {
valueType = this.toCSharpType(mapValueType);
}
else if (ModelUtil.isScalar(mapDeclaration.getValue())) {
const scalarDeclaration = mapDeclaration.getModelFile().getType(mapDeclaration.getValue().getType());
const keyFQN = ModelUtil.removeNamespaceVersionFromFullyQualifiedName(scalarDeclaration.getFullyQualifiedName());
const scalarType = keyFQN === 'concerto.scalar.UUID' ? keyFQN : scalarDeclaration.getType();
valueType = this.toCSharpType(scalarType);
} else {
valueType = mapValueType;
}

parameters.fileWriter.writeLine(1, `public Dictionary<${keyType}, ${valueType}> ${field.getName()} { get; set; }`);
return null;
}

Expand Down
8 changes: 4 additions & 4 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public class Company : Concept {
public Dictionary<string, Employee> employeeDirectory { get; set; }
public Dictionary<string, TShirtSizeType> employeeTShirtSizes { get; set; }
public Dictionary<string, Concept> employeeProfiles { get; set; }
public Dictionary<string, SSN> employeeSocialSecurityNumbers { get; set; }
public Dictionary<string, string> employeeSocialSecurityNumbers { get; set; }
}
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public enum Department {
Expand Down Expand Up @@ -428,7 +428,7 @@ public abstract class Person : Participant {
public string ssn { get; set; }
public float height { get; set; }
public System.DateTime dob { get; set; }
public Dictionary<string, KinTelephone> nextOfKin { get; set; }
public Dictionary<string, string> nextOfKin { get; set; }
}
[AccordProject.Concerto.Type(Namespace = "org.acme.hr", Version = null, Name = "Employee")]
[System.Text.Json.Serialization.JsonConverter(typeof(AccordProject.Concerto.ConcertoConverterFactorySystem))]
Expand Down Expand Up @@ -5932,7 +5932,7 @@ public class Company : Concept {
public Dictionary<string, Employee> employeeDirectory { get; set; }
public Dictionary<string, TShirtSizeType> employeeTShirtSizes { get; set; }
public Dictionary<string, Concept> employeeProfiles { get; set; }
public Dictionary<string, SSN> employeeSocialSecurityNumbers { get; set; }
public Dictionary<string, string> employeeSocialSecurityNumbers { get; set; }
}
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public enum Department {
Expand Down Expand Up @@ -5978,7 +5978,7 @@ public abstract class Person : Participant {
public string ssn { get; set; }
public float height { get; set; }
public System.DateTime dob { get; set; }
public Dictionary<string, KinTelephone> nextOfKin { get; set; }
public Dictionary<string, string> nextOfKin { get; set; }
}
Copy link
Member

Choose a reason for hiding this comment

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

These types seem to have got more general. Is this expected?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the underlying Primitive of a Scalar should be printed, in place of its alias name

Copy link
Contributor

Choose a reason for hiding this comment

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

Beware the logic that handles UUID. Did you test with a map that has a UUID as a key or value? It looks like the code generator doesn't unbox those.

Copy link
Member Author

Choose a reason for hiding this comment

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

@dselman logic added to handle concerto.scalar.UUID for k & v

[AccordProject.Concerto.Type(Namespace = "org.acme.hr", Version = "1.0.0", Name = "Employee")]
[System.Text.Json.Serialization.JsonConverter(typeof(AccordProject.Concerto.ConcertoConverterFactorySystem))]
Expand Down
52 changes: 51 additions & 1 deletion test/codegen/fromcto/csharp/csharpvisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,7 @@ public class SampleModel : Concept {
};
sandbox.restore();
sandbox.stub(ModelUtil, 'isMap').callsFake(() => {
return true;
return false;
});

});
Expand Down Expand Up @@ -1509,6 +1509,11 @@ public class SampleModel : Concept {
mockField.dummy = 'Dummy Value';
mockField.getModelFile.returns({ getType: getType });

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

const mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();
Expand All @@ -1533,11 +1538,22 @@ public class SampleModel : Concept {
mockField.dummy = 'Dummy Value';
mockField.getModelFile.returns({ getType: getType });

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

let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
let modelFile = sinon.createStubInstance(ModelFile);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();

mockField.getModelFile.returns(modelFile);
modelFile.getType.returns(mockMapDeclaration);

getType.returns(mockMapDeclaration);
getKeyType.returns('String');
getValueType.returns('Concept');
Expand All @@ -1558,6 +1574,40 @@ public class SampleModel : Concept {
mockField.dummy = 'Dummy Value';
mockField.getModelFile.returns({ getType: getType });

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

let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();

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

csharpVisitor.visitField(mockField, param);
param.fileWriter.writeLine.withArgs(1, 'public Dictionary<string, System.DateTime> Map1 { get; set; }').calledOnce.should.be.ok;
});

it('should write a line for field name and type thats a map of <SSN, DateTime>', () => {
const mockField = sinon.createStubInstance(Field);
const getType = sinon.stub();

mockField.dummy = 'Dummy Value';
mockField.getModelFile.returns({ getType: getType });

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

let mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();
Expand Down
7 changes: 0 additions & 7 deletions types/lib/codegen/fromcto/golang/golangvisitor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,4 @@ declare class GoLangVisitor {
* @private
*/
private toGoPackageName;
/**
* Check if field is a Map Declaration
* @param {Field} field - the field being visited
* @return {boolean} true if field is a Map Declaration
* @private
*/
private isMap;
}
Loading