mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2026-03-18 08:29:15 +00:00
Add nullable support to C# client (refactor) (#1775)
* add nullable support to c# client (refactor) * clean up methods * move typemapping to constructor
This commit is contained in:
@@ -75,6 +75,12 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
protected Set<String> collectionTypes;
|
||||
protected Set<String> mapTypes;
|
||||
|
||||
// true if support nullable type
|
||||
protected boolean supportNullable = Boolean.FALSE;
|
||||
|
||||
// nullable type
|
||||
protected Set<String> nullableType = new HashSet<String>();
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCSharpCodegen.class);
|
||||
|
||||
public AbstractCSharpCodegen() {
|
||||
@@ -130,19 +136,26 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
"String",
|
||||
"string",
|
||||
"bool?",
|
||||
"bool",
|
||||
"double?",
|
||||
"double",
|
||||
"decimal?",
|
||||
"decimal",
|
||||
"int?",
|
||||
"int",
|
||||
"long?",
|
||||
"long",
|
||||
"float?",
|
||||
"float",
|
||||
"byte[]",
|
||||
"ICollection",
|
||||
"Collection",
|
||||
"List",
|
||||
"Dictionary",
|
||||
"DateTime?",
|
||||
"DateTime",
|
||||
"DateTimeOffset?",
|
||||
"String",
|
||||
"DataTimeOffset",
|
||||
"Boolean",
|
||||
"Double",
|
||||
"Int32",
|
||||
@@ -157,6 +170,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
instantiationTypes.put("list", "List");
|
||||
instantiationTypes.put("map", "Dictionary");
|
||||
|
||||
|
||||
// Nullable types here assume C# 2 support is not part of base
|
||||
typeMapping = new HashMap<String, String>();
|
||||
typeMapping.put("string", "string");
|
||||
@@ -176,6 +190,11 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
typeMapping.put("map", "Dictionary");
|
||||
typeMapping.put("object", "Object");
|
||||
typeMapping.put("UUID", "Guid?");
|
||||
|
||||
// nullable type
|
||||
nullableType = new HashSet<String>(
|
||||
Arrays.asList("decimal", "bool", "int", "float", "long", "double", "DateTime", "Guid")
|
||||
);
|
||||
}
|
||||
|
||||
public void setReturnICollection(boolean returnICollection) {
|
||||
@@ -209,8 +228,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
this.useDateTimeOffsetFlag = flag;
|
||||
if (flag) {
|
||||
typeMapping.put("DateTime", "DateTimeOffset?");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
typeMapping.put("DateTime", "DateTime?");
|
||||
}
|
||||
}
|
||||
@@ -421,8 +439,8 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : models.entrySet()) {
|
||||
String swaggerName = entry.getKey();
|
||||
CodegenModel model = ModelUtils.getModelByName(swaggerName, models);
|
||||
String openAPIName = entry.getKey();
|
||||
CodegenModel model = ModelUtils.getModelByName(openAPIName, models);
|
||||
if (model != null) {
|
||||
for (CodegenProperty var : model.allVars) {
|
||||
if (enumRefs.containsKey(var.dataType)) {
|
||||
@@ -483,7 +501,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Expected to retrieve model %s by name, but no model was found. Check your -Dmodels inclusions.", swaggerName);
|
||||
LOGGER.warn("Expected to retrieve model %s by name, but no model was found. Check your -Dmodels inclusions.", openAPIName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,28 +591,30 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
}
|
||||
}
|
||||
|
||||
for (CodegenParameter parameter: operation.allParams) {
|
||||
CodegenModel model = null;
|
||||
for(Object modelHashMap: allModels) {
|
||||
CodegenModel codegenModel = ((HashMap<String, CodegenModel>) modelHashMap).get("model");
|
||||
if (codegenModel.getClassname().equals(parameter.dataType)) {
|
||||
model = codegenModel;
|
||||
break;
|
||||
if (!isSupportNullable()) {
|
||||
for (CodegenParameter parameter : operation.allParams) {
|
||||
CodegenModel model = null;
|
||||
for (Object modelHashMap : allModels) {
|
||||
CodegenModel codegenModel = ((HashMap<String, CodegenModel>) modelHashMap).get("model");
|
||||
if (codegenModel.getClassname().equals(parameter.dataType)) {
|
||||
model = codegenModel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (model == null) {
|
||||
// Primitive data types all come already marked
|
||||
parameter.isNullable = true;
|
||||
} else {
|
||||
// Effectively mark enum models as enums and non-nullable
|
||||
if (model.isEnum) {
|
||||
parameter.isEnum = true;
|
||||
parameter.allowableValues = model.allowableValues;
|
||||
parameter.isPrimitiveType = true;
|
||||
parameter.isNullable = false;
|
||||
} else {
|
||||
if (model == null) {
|
||||
// Primitive data types all come already marked
|
||||
parameter.isNullable = true;
|
||||
} else {
|
||||
// Effectively mark enum models as enums and non-nullable
|
||||
if (model.isEnum) {
|
||||
parameter.isEnum = true;
|
||||
parameter.allowableValues = model.allowableValues;
|
||||
parameter.isPrimitiveType = true;
|
||||
parameter.isNullable = false;
|
||||
} else {
|
||||
parameter.isNullable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -792,6 +812,14 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
return reservedWords.contains(word);
|
||||
}
|
||||
|
||||
public String getNullableType(Schema p, String type) {
|
||||
if (languageSpecificPrimitives.contains(type)) {
|
||||
return type;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchemaType(Schema p) {
|
||||
String openAPIType = super.getSchemaType(p);
|
||||
@@ -804,8 +832,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
|
||||
if (typeMapping.containsKey(openAPIType)) {
|
||||
type = typeMapping.get(openAPIType);
|
||||
if (languageSpecificPrimitives.contains(type)) {
|
||||
return type;
|
||||
String languageType = getNullableType(p, type);
|
||||
if (languageType != null) {
|
||||
return languageType;
|
||||
}
|
||||
} else {
|
||||
type = openAPIType;
|
||||
@@ -950,6 +979,14 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
this.interfacePrefix = interfacePrefix;
|
||||
}
|
||||
|
||||
public boolean isSupportNullable() {
|
||||
return supportNullable;
|
||||
}
|
||||
|
||||
public void setSupportNullable(final boolean supportNullable) {
|
||||
this.supportNullable = supportNullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toEnumValue(String value, String datatype) {
|
||||
// C# only supports enums as literals for int, int?, long, long?, byte, and byte?. All else must be treated as strings.
|
||||
@@ -1011,7 +1048,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
|
||||
@Override
|
||||
public boolean isDataTypeString(String dataType) {
|
||||
// also treat double/decimal/float as "string" in enum so that the values (e.g. 2.8) get double-quoted
|
||||
return "String".equalsIgnoreCase(dataType) || "double?".equals(dataType) || "decimal?".equals(dataType) || "float?".equals(dataType);
|
||||
return "String".equalsIgnoreCase(dataType) ||
|
||||
"double?".equals(dataType) || "decimal?".equals(dataType) || "float?".equals(dataType) ||
|
||||
"double".equals(dataType) || "decimal".equals(dataType) || "float".equals(dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -776,7 +776,6 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.samskivert.mustache.Mustache;
|
||||
|
||||
import io.swagger.v3.oas.models.media.ArraySchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
|
||||
import org.openapitools.codegen.CliOption;
|
||||
@@ -32,6 +33,7 @@ import org.openapitools.codegen.CodegenParameter;
|
||||
import org.openapitools.codegen.CodegenProperty;
|
||||
import org.openapitools.codegen.CodegenType;
|
||||
import org.openapitools.codegen.SupportingFile;
|
||||
import org.openapitools.codegen.utils.ModelUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -84,19 +86,39 @@ public class CSharpRefactorClientCodegen extends AbstractCSharpCodegen {
|
||||
// By default, generated code is considered public
|
||||
protected boolean nonPublicApi = Boolean.FALSE;
|
||||
|
||||
|
||||
public CSharpRefactorClientCodegen() {
|
||||
super();
|
||||
|
||||
// mapped non-nullable type without ?
|
||||
typeMapping = new HashMap<String, String>();
|
||||
typeMapping.put("string", "string");
|
||||
typeMapping.put("binary", "byte[]");
|
||||
typeMapping.put("ByteArray", "byte[]");
|
||||
typeMapping.put("boolean", "bool");
|
||||
typeMapping.put("integer", "int");
|
||||
typeMapping.put("float", "float");
|
||||
typeMapping.put("long", "long");
|
||||
typeMapping.put("double", "double");
|
||||
typeMapping.put("number", "decimal");
|
||||
typeMapping.put("DateTime", "DateTime");
|
||||
typeMapping.put("date", "DateTime");
|
||||
typeMapping.put("file", "System.IO.Stream");
|
||||
typeMapping.put("array", "List");
|
||||
typeMapping.put("list", "List");
|
||||
typeMapping.put("map", "Dictionary");
|
||||
typeMapping.put("object", "Object");
|
||||
typeMapping.put("UUID", "Guid");
|
||||
|
||||
setSupportNullable(Boolean.TRUE);
|
||||
hideGenerationTimestamp = Boolean.TRUE;
|
||||
supportsInheritance = true;
|
||||
modelTemplateFiles.put("model.mustache", ".cs");
|
||||
apiTemplateFiles.put("api.mustache", ".cs");
|
||||
|
||||
modelDocTemplateFiles.put("model_doc.mustache", ".md");
|
||||
apiDocTemplateFiles.put("api_doc.mustache", ".md");
|
||||
|
||||
embeddedTemplateDir = templateDir = "csharp-refactor";
|
||||
|
||||
hideGenerationTimestamp = Boolean.TRUE;
|
||||
|
||||
cliOptions.clear();
|
||||
|
||||
// CLI options
|
||||
@@ -223,7 +245,6 @@ public class CSharpRefactorClientCodegen extends AbstractCSharpCodegen {
|
||||
setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
|
||||
}
|
||||
|
||||
|
||||
if (isEmpty(apiPackage)) {
|
||||
setApiPackage("Api");
|
||||
}
|
||||
@@ -609,6 +630,10 @@ public class CSharpRefactorClientCodegen extends AbstractCSharpCodegen {
|
||||
public void postProcessParameter(CodegenParameter parameter) {
|
||||
postProcessPattern(parameter.pattern, parameter.vendorExtensions);
|
||||
super.postProcessParameter(parameter);
|
||||
|
||||
if (!parameter.required && nullableType.contains(parameter.dataType)) { //optional
|
||||
parameter.dataType = parameter.dataType + "?";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -852,4 +877,17 @@ public class CSharpRefactorClientCodegen extends AbstractCSharpCodegen {
|
||||
// To avoid unexpected behaviors when options are passed programmatically such as { "supportsAsync": "" }
|
||||
return super.processCompiler(compiler).emptyStringIsFalse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableType(Schema p, String type) {
|
||||
if (languageSpecificPrimitives.contains(type)) {
|
||||
if (isSupportNullable() && ModelUtils.isNullable(p) && nullableType.contains(type)) {
|
||||
return type + "?";
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user