patch all property collections (#15678)

This commit is contained in:
devhl-labs 2023-05-30 12:36:35 -04:00 committed by GitHub
parent 1e1e786a72
commit 0bcf9d8bde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -519,10 +519,13 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
final Map<String, ModelsMap> processed = super.postProcessAllModels(objs);
// TODO: move the logic of these three methods into patchProperty so all CodegenProperty instances get the same treatment
postProcessEnumRefs(processed);
updateValueTypeProperty(processed);
updateNullableTypeProperty(processed);
Map<String, CodegenModel> enumRefs = new HashMap<>();
for (Map.Entry<String, ModelsMap> entry : processed.entrySet()) {
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), processed);
if (model.isEnum) {
enumRefs.put(model.getClassname(), model);
}
}
for (Map.Entry<String, ModelsMap> entry : objs.entrySet()) {
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), objs);
@ -532,34 +535,52 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
// TODO: why do these collections contain different instances?
// fixing allVars should suffice instead of patching every collection
for (CodegenProperty property : model.allVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.vars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.readWriteVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.optionalVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.parentVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.requiredVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.readOnlyVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
for (CodegenProperty property : model.nonNullableVars) {
patchProperty(model, property);
patchProperty(enumRefs, model, property);
}
}
return processed;
}
private void patchProperty(CodegenModel model, CodegenProperty property) {
private void patchProperty(Map<String, CodegenModel> enumRefs, CodegenModel model, CodegenProperty property) {
if (enumRefs.containsKey(property.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(property.dataType);
property.allowableValues = refModel.allowableValues;
property.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
property.isPrimitiveType = true;
}
if (!property.isContainer && (nullableType.contains(property.dataType) || property.isEnum)) {
property.vendorExtensions.put("x-csharp-value-type", true);
}
property.vendorExtensions.put("x-is-value-type", isValueType(property));
String tmpPropertyName = escapeReservedWord(model, property.name);
if (property.name != tmpPropertyName) {
// the casing will be wrong if we just set the name to escapeReservedWord
@ -646,137 +667,6 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
return enumVars;
}
/**
* C# differs from other languages in that Enums are not _true_ objects; enums are compiled to integral types.
* So, in C#, an enum is considers more like a user-defined primitive.
* <p>
* When working with enums, we can't always assume a RefModel is a nullable type (where default(YourType) == null),
* so this post processing runs through all models to find RefModel'd enums. Then, it runs through all vars and modifies
* those vars referencing RefModel'd enums to work the same as inlined enums rather than as objects.
*
* @param models processed models to be further processed for enum references
*/
private void postProcessEnumRefs(final Map<String, ModelsMap> models) {
Map<String, CodegenModel> enumRefs = new HashMap<>();
for (Map.Entry<String, ModelsMap> entry : models.entrySet()) {
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), models);
if (model.isEnum) {
enumRefs.put(model.getClassname(), model);
}
}
for (String openAPIName : models.keySet()) {
CodegenModel model = ModelUtils.getModelByName(openAPIName, models);
if (model != null) {
for (CodegenProperty var : model.allVars) {
if (enumRefs.containsKey(var.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(var.dataType);
var.allowableValues = refModel.allowableValues;
var.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
var.isPrimitiveType = true;
}
}
for (CodegenProperty var : model.vars) {
if (enumRefs.containsKey(var.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(var.dataType);
var.allowableValues = refModel.allowableValues;
var.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
var.isPrimitiveType = true;
}
}
for (CodegenProperty var : model.readWriteVars) {
if (enumRefs.containsKey(var.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(var.dataType);
var.allowableValues = refModel.allowableValues;
var.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
var.isPrimitiveType = true;
}
}
for (CodegenProperty var : model.readOnlyVars) {
if (enumRefs.containsKey(var.dataType)) {
// Handle any enum properties referred to by $ref.
// This is different in C# than most other generators, because enums in C# are compiled to integral types,
// while enums in many other languages are true objects.
CodegenModel refModel = enumRefs.get(var.dataType);
var.allowableValues = refModel.allowableValues;
var.isEnum = true;
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
var.isPrimitiveType = true;
}
}
/* Comment out the following as model.dataType is always the model name, eg. OuterIntegerEnum,
* and this will fix the integer enum via #9035.
* Only x-enum-byte is used in the template but it won't work due to the bug mentioned above.
* A better solution is to introduce isLong, isInteger, etc in the DefaultCodegen
* so that there is no need for each generator to post-process model enums.
*
// We're looping all models here.
if (model.isEnum) {
// We now need to make allowableValues.enumVars look like the context of CodegenProperty
Boolean isString = false;
Boolean isInteger = false;
Boolean isLong = false;
Boolean isByte = false;
if (model.dataType.startsWith("byte")) {
// C# Actually supports byte and short enums, swagger spec only supports byte.
isByte = true;
model.vendorExtensions.put("x-enum-byte", true);
} else if (model.dataType.startsWith("int32")) {
isInteger = true;
model.vendorExtensions.put("x-enum-integer", true);
} else if (model.dataType.startsWith("int64")) {
isLong = true;
model.vendorExtensions.put("x-enum-long", true);
} else {
// C# doesn't support non-integral enums, so we need to treat everything else as strings (e.g. to not lose precision or data integrity)
isString = true;
model.vendorExtensions.put("x-enum-string", true);
}
// Since we iterate enumVars for modelInnerEnum and enumClass templates, and CodegenModel is missing some of CodegenProperty's properties,
// we can take advantage of Mustache's contextual lookup to add the same "properties" to the model's enumVars scope rather than CodegenProperty's scope.
List<Map<String, String>> enumVars = (ArrayList<Map<String, String>>) model.allowableValues.get("enumVars");
List<Map<String, Object>> newEnumVars = new ArrayList<Map<String, Object>>();
for (Map<String, String> enumVar : enumVars) {
Map<String, Object> mixedVars = new HashMap<String, Object>();
mixedVars.putAll(enumVar);
mixedVars.put("isString", isString);
mixedVars.put("isLong", isLong);
mixedVars.put("isInteger", isInteger);
mixedVars.put("isByte", isByte);
newEnumVars.add(mixedVars);
}
if (!newEnumVars.isEmpty()) {
model.allowableValues.put("enumVars", newEnumVars);
}
} */
} else {
LOGGER.warn("Expected to retrieve model %s by name, but no model was found. Check your -Dmodels inclusions.", openAPIName);
}
}
}
/**
* Update codegen property's enum by adding "enumVars" (with name and value)
*
@ -813,49 +703,6 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
}
}
/**
* Update property if it is a C# value type
*
* @param models list of all models
*/
protected void updateValueTypeProperty(Map<String, ModelsMap> models) {
for (String openAPIName : models.keySet()) {
CodegenModel model = ModelUtils.getModelByName(openAPIName, models);
if (model != null) {
for (CodegenProperty var : model.vars) {
var.vendorExtensions.put("x-is-value-type", isValueType(var));
}
}
}
}
/**
* Update property if it is a C# nullable type
*
* @param models list of all models
*/
protected void updateNullableTypeProperty(Map<String, ModelsMap> models) {
for (String openAPIName : models.keySet()) {
CodegenModel model = ModelUtils.getModelByName(openAPIName, models);
if (model != null) {
for (CodegenProperty var : model.vars) {
if (!var.isContainer && (nullableType.contains(var.dataType) || var.isEnum)) {
var.vendorExtensions.put("x-csharp-value-type", true);
}
}
// https://github.com/OpenAPITools/openapi-generator/issues/12324
// we should not need to iterate both vars and allVars
// the collections dont have the same instance, so we have to do it again
for (CodegenProperty var : model.allVars) {
if (!var.isContainer && (nullableType.contains(var.dataType) || var.isEnum)) {
var.vendorExtensions.put("x-csharp-value-type", true);
}
}
}
}
}
@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
super.postProcessOperationsWithModels(objs, allModels);