forked from loafle/openapi-generator-original
[csharp] Treat enum models consistently (#6851)
* [csharp] Treat enum models consistently C# works differently from most languages in that enums are not considered objects. This means default(EnumType) will choose a default of the first enum option. This isn't desirable because it breaks the required = false functionality of swagger specs, which defines a property which isn't required to exist in the message body. Rather than force consumers to use enum values such as UNSPECIFIED, UNKNOWN, NOT_SET, etc... we can treat enums as primitives. This means any non-required enum will become Nullable<EnumType> regardless of whether it is defined as an inline enum or a referenced enum model. * Categorizing C# integration test for enums as general * [csharp] Remove enum-ref integration test * [csharp] Clean up general enum support integration test, validate different enum usage cases.
This commit is contained in:
@@ -4,6 +4,14 @@ package io.swagger.codegen;
|
||||
* A class for storing constants that are used throughout the project.
|
||||
*/
|
||||
public class CodegenConstants {
|
||||
public static final String APIS = "apis";
|
||||
public static final String MODELS = "models";
|
||||
public static final String SUPPORTING_FILES = "supportingFiles";
|
||||
public static final String MODEL_TESTS = "modelTests";
|
||||
public static final String MODEL_DOCS = "modelDocs";
|
||||
public static final String API_TESTS = "apiTests";
|
||||
public static final String API_DOCS = "apiDocs";
|
||||
|
||||
public static final String API_PACKAGE = "apiPackage";
|
||||
public static final String API_PACKAGE_DESC = "package for generated api classes";
|
||||
|
||||
|
||||
@@ -33,9 +33,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
private Boolean generateApiDocumentation = null;
|
||||
private Boolean generateModelTests = null;
|
||||
private Boolean generateModelDocumentation = null;
|
||||
private Boolean generateSwaggerMetadata = true;
|
||||
private String basePath;
|
||||
private String basePathWithoutHost;
|
||||
private String contextPath;
|
||||
private Map<String, String> generatorPropertyDefaults = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Generator opts(ClientOptInput opts) {
|
||||
@@ -61,6 +63,38 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically disable the output of .swagger-codegen/VERSION, .swagger-codegen-ignore,
|
||||
* or other metadata files used by Swagger Codegen.
|
||||
* @param generateSwaggerMetadata true: enable outputs, false: disable outputs
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public void setGenerateSwaggerMetadata(Boolean generateSwaggerMetadata) {
|
||||
this.generateSwaggerMetadata = generateSwaggerMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set generator properties otherwise pulled from system properties.
|
||||
* Useful for running tests in parallel without relying on System.properties.
|
||||
* @param key The system property key
|
||||
* @param value The system property value
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public void setGeneratorPropertyDefault(final String key, final String value) {
|
||||
this.generatorPropertyDefaults.put(key, value);
|
||||
}
|
||||
|
||||
private Boolean getGeneratorPropertyDefaultSwitch(final String key, final Boolean defaultValue) {
|
||||
String result = null;
|
||||
if (this.generatorPropertyDefaults.containsKey(key)) {
|
||||
result = this.generatorPropertyDefaults.get(key);
|
||||
}
|
||||
if (result != null) {
|
||||
return Boolean.valueOf(result);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private String getScheme() {
|
||||
String scheme;
|
||||
if (swagger.getSchemes() != null && swagger.getSchemes().size() > 0) {
|
||||
@@ -88,11 +122,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
private void configureGeneratorProperties() {
|
||||
|
||||
// allows generating only models by specifying a CSV of models to generate, or empty for all
|
||||
generateApis = System.getProperty("apis") != null ? true : null;
|
||||
generateModels = System.getProperty("models") != null ? true : null;
|
||||
generateSupportingFiles = System.getProperty("supportingFiles") != null ? true : null;
|
||||
// NOTE: Boolean.TRUE is required below rather than `true` because of JVM boxing constraints and type inference.
|
||||
generateApis = System.getProperty(CodegenConstants.APIS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.APIS, null);
|
||||
generateModels = System.getProperty(CodegenConstants.MODELS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODELS, null);
|
||||
generateSupportingFiles = System.getProperty(CodegenConstants.SUPPORTING_FILES) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.SUPPORTING_FILES, null);
|
||||
|
||||
if (generateApis == null && generateModels == null && generateSupportingFiles == null) {
|
||||
// no specifics are set, generate everything
|
||||
@@ -110,10 +144,10 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
// model/api tests and documentation options rely on parent generate options (api or model) and no other options.
|
||||
// They default to true in all scenarios and can only be marked false explicitly
|
||||
generateModelTests = System.getProperty("modelTests") != null ? Boolean.valueOf(System.getProperty("modelTests")) : true;
|
||||
generateModelDocumentation = System.getProperty("modelDocs") != null ? Boolean.valueOf(System.getProperty("modelDocs")) : true;
|
||||
generateApiTests = System.getProperty("apiTests") != null ? Boolean.valueOf(System.getProperty("apiTests")) : true;
|
||||
generateApiDocumentation = System.getProperty("apiDocs") != null ? Boolean.valueOf(System.getProperty("apiDocs")) : true;
|
||||
generateModelTests = System.getProperty(CodegenConstants.MODEL_TESTS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.MODEL_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_TESTS, true);
|
||||
generateModelDocumentation = System.getProperty(CodegenConstants.MODEL_DOCS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.MODEL_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_DOCS, true);
|
||||
generateApiTests = System.getProperty(CodegenConstants.API_TESTS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.API_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_TESTS, true);
|
||||
generateApiDocumentation = System.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true);
|
||||
|
||||
|
||||
// Additional properties added for tests to exclude references in project related files
|
||||
@@ -595,7 +629,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
final String swaggerCodegenIgnore = ".swagger-codegen-ignore";
|
||||
String ignoreFileNameTarget = config.outputFolder() + File.separator + swaggerCodegenIgnore;
|
||||
File ignoreFile = new File(ignoreFileNameTarget);
|
||||
if (!ignoreFile.exists()) {
|
||||
if (generateSwaggerMetadata && !ignoreFile.exists()) {
|
||||
String ignoreFileNameSource = File.separator + config.getCommonTemplateDir() + File.separator + swaggerCodegenIgnore;
|
||||
String ignoreFileContents = readResourceContents(ignoreFileNameSource);
|
||||
try {
|
||||
@@ -606,13 +640,15 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
files.add(ignoreFile);
|
||||
}
|
||||
|
||||
final String swaggerVersionMetadata = config.outputFolder() + File.separator + ".swagger-codegen" + File.separator + "VERSION";
|
||||
File swaggerVersionMetadataFile = new File(swaggerVersionMetadata);
|
||||
try {
|
||||
writeToFile(swaggerVersionMetadata, ImplementationVersion.read());
|
||||
files.add(swaggerVersionMetadataFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not generate supporting file '" + swaggerVersionMetadata + "'", e);
|
||||
if(generateSwaggerMetadata) {
|
||||
final String swaggerVersionMetadata = config.outputFolder() + File.separator + ".swagger-codegen" + File.separator + "VERSION";
|
||||
File swaggerVersionMetadataFile = new File(swaggerVersionMetadata);
|
||||
try {
|
||||
writeToFile(swaggerVersionMetadata, ImplementationVersion.read());
|
||||
files.add(swaggerVersionMetadataFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not generate supporting file '" + swaggerVersionMetadata + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.swagger.codegen.languages;
|
||||
|
||||
import io.swagger.codegen.*;
|
||||
import io.swagger.codegen.utils.ModelUtils;
|
||||
import io.swagger.models.properties.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -296,6 +297,11 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
|
||||
super.postProcessModelProperty(model, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
List<Object> models = (List<Object>) objs.get("models");
|
||||
@@ -315,6 +321,61 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
return postProcessModelsEnum(objs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by {@link DefaultGenerator} after all models have been post-processed, allowing for a last pass of codegen-specific model cleanup.
|
||||
*
|
||||
* @param objs Current state of codegen object model.
|
||||
* @return An in-place modified state of the codegen object model.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||
final Map<String, Object> processed = super.postProcessAllModels(objs);
|
||||
postProcessEnumRefs(processed);
|
||||
return processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
private void postProcessEnumRefs(final Map<String, Object> models) {
|
||||
Map<String, CodegenModel> enumRefs = new HashMap<String, CodegenModel>();
|
||||
for (Map.Entry<String, Object> entry : models.entrySet()) {
|
||||
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), models);
|
||||
if (model.isEnum) {
|
||||
enumRefs.put(entry.getKey(), model);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : models.entrySet()) {
|
||||
String swaggerName = entry.getKey();
|
||||
CodegenModel model = ModelUtils.getModelByName(swaggerName, 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;
|
||||
updateCodegenPropertyEnum(var);
|
||||
|
||||
// We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#.
|
||||
var.isPrimitiveType = true;
|
||||
var.isEnum = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Expected to retrieve model %s by name, but no model was found. Check your -Dmodels inclusions.", swaggerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
|
||||
super.postProcessOperations(objs);
|
||||
|
||||
@@ -513,11 +513,6 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
|
||||
this.packageGuid = packageGuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objMap) {
|
||||
return super.postProcessModels(objMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessParameter(CodegenParameter parameter) {
|
||||
postProcessPattern(parameter.pattern, parameter.vendorExtensions);
|
||||
@@ -530,7 +525,6 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
|
||||
super.postProcessModelProperty(model, property);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The swagger pattern spec follows the Perl convention and style of modifiers. .NET
|
||||
* does not support this syntax directly so we need to convert the pattern to a .NET compatible
|
||||
|
||||
Reference in New Issue
Block a user