diff --git a/docs/generators.md b/docs/generators.md index c1f09094dbc..9553e01d7ce 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -81,7 +81,7 @@ The following generators are available: * [cpp-pistache-server](generators/cpp-pistache-server.md) * [cpp-qt-qhttpengine-server](generators/cpp-qt-qhttpengine-server.md) * [cpp-restbed-server](generators/cpp-restbed-server.md) -* [csharp-netcore-functions (beta)](generators/csharp-netcore-functions.md) +* [csharp-netcore-functions](generators/csharp-netcore-functions.md) * [erlang-server](generators/erlang-server.md) * [fsharp-functions (beta)](generators/fsharp-functions.md) * [fsharp-giraffe-server (beta)](generators/fsharp-giraffe-server.md) diff --git a/docs/generators/csharp-netcore-functions.md b/docs/generators/csharp-netcore-functions.md index 5030dee051a..bede0fc8f24 100644 --- a/docs/generators/csharp-netcore-functions.md +++ b/docs/generators/csharp-netcore-functions.md @@ -1,6 +1,8 @@ --- title: Documentation for the csharp-netcore-functions Generator --- +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. +======= ## METADATA @@ -15,7 +17,6 @@ title: Documentation for the csharp-netcore-functions Generator ## CONFIG OPTIONS These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. - | Option | Description | Values | Default | | ------ | ----------- | ------ | ------- | |allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CsharpNetcoreFunctionsServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CsharpNetcoreFunctionsServerCodegen.java index 8e020f3e641..02ff5cbf273 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CsharpNetcoreFunctionsServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CsharpNetcoreFunctionsServerCodegen.java @@ -16,90 +16,589 @@ package org.openapitools.codegen.languages; +import com.samskivert.mustache.Mustache; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.parser.util.SchemaTypeUtil; import org.openapitools.codegen.*; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.parameters.Parameter; - -import java.io.File; -import java.util.*; - -import org.apache.commons.lang3.StringUtils; - -import org.openapitools.codegen.meta.GeneratorMetadata; -import org.openapitools.codegen.meta.Stability; +import org.openapitools.codegen.meta.features.*; +import org.openapitools.codegen.utils.ModelUtils; +import org.openapitools.codegen.utils.URLPathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CsharpNetcoreFunctionsServerCodegen extends CSharpNetCoreReducedClientCodegen { - public static final String PROJECT_NAME = "projectName"; +import java.io.File; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; - final Logger LOGGER = LoggerFactory.getLogger(CsharpNetcoreFunctionsServerCodegen.class); +import static java.util.UUID.randomUUID; - public CodegenType getTag() { - return CodegenType.SERVER; - } +public class CsharpNetcoreFunctionsServerCodegen extends AbstractCSharpCodegen { - public String getName() { - return "csharp-netcore-functions"; - } + public static final String NET_CORE_VERSION = "netCoreVersion"; + public static final String AZURE_FUNCTIONS_VERSION = "azureFunctionsVersion"; + public static final String CLASS_MODIFIER = "classModifier"; + public static final String OPERATION_MODIFIER = "operationModifier"; + public static final String OPERATION_IS_ASYNC = "operationIsAsync"; + public static final String OPERATION_RESULT_TASK = "operationResultTask"; + public static final String GENERATE_BODY = "generateBody"; + public static final String BUILD_TARGET = "buildTarget"; + public static final String MODEL_CLASS_MODIFIER = "modelClassModifier"; + public static final String TARGET_FRAMEWORK= "targetFramework"; + public static final String FUNCTIONS_SDK_VERSION = "functionsSDKVersion"; - public String getHelp() { - return "Generates a csharp server."; - } + public static final String COMPATIBILITY_VERSION = "compatibilityVersion"; + public static final String USE_NEWTONSOFT = "useNewtonsoft"; + public static final String NEWTONSOFT_VERSION = "newtonsoftVersion"; + + private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}"; + private String userSecretsGuid = randomUUID().toString(); + + protected final Logger LOGGER = LoggerFactory.getLogger(AspNetCoreServerCodegen.class); + + protected int serverPort = 8080; + protected String serverHost = "0.0.0.0"; + protected CliOption netCoreVersion = new CliOption(NET_CORE_VERSION, ".NET Core version: 6.0, 5.0, 3.1, 3.0"); + protected CliOption azureFunctionsVersion = new CliOption(AZURE_FUNCTIONS_VERSION, "Azure functions version: v4, v3"); + private CliOption classModifier = new CliOption(CLASS_MODIFIER, "Class Modifier for function classes: Empty string or abstract."); + private CliOption operationModifier = new CliOption(OPERATION_MODIFIER, "Operation Modifier can be virtual or abstract"); + private CliOption modelClassModifier = new CliOption(MODEL_CLASS_MODIFIER, "Model Class Modifier can be nothing or partial"); + private boolean generateBody = true; + private CliOption buildTarget = new CliOption("buildTarget", "Target to build an application or library"); + private String projectSdk = "Microsoft.NET.Sdk"; + private boolean operationIsAsync = false; + private boolean operationResultTask = false; + private boolean isLibrary = false; + private boolean useFrameworkReference = false; + private boolean useNewtonsoft = true; + private String newtonsoftVersion = "3.0.0"; public CsharpNetcoreFunctionsServerCodegen() { super(); - generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) - .stability(Stability.BETA) - .build(); + // TODO: AspnetCore community review + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .excludeWireFormatFeatures(WireFormatFeature.PROTOBUF) + .includeSecurityFeatures( + SecurityFeature.ApiKey, + SecurityFeature.BasicAuth, + SecurityFeature.BearerToken + ) + .excludeSecurityFeatures( + SecurityFeature.OpenIDConnect, + SecurityFeature.OAuth2_Password, + SecurityFeature.OAuth2_AuthorizationCode, + SecurityFeature.OAuth2_ClientCredentials, + SecurityFeature.OAuth2_Implicit + ) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling, + GlobalFeature.MultiServer + ) + .includeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .includeParameterFeatures( + ParameterFeature.Cookie + ) + ); + + outputFolder = "generated-code" + File.separator + getName(); - outputFolder = "generated-code" + File.separator + "csharp"; modelTemplateFiles.put("model.mustache", ".cs"); - apiTemplateFiles.put("functions.mustache", ".cs"); - embeddedTemplateDir = templateDir = "csharp-netcore-functions"; - apiPackage = "Apis"; - modelPackage = "Models"; - String clientPackageDir = "generatedSrc/Client"; - supportingFiles.add(new SupportingFile("README.mustache", "generatedSrc", "README.md")); - supportingFiles.add(new SupportingFile("project.mustache", "generatedSrc", "project.json")); + apiTemplateFiles.put("function.mustache", ".cs"); - supportingFiles.add(new SupportingFile("IApiAccessor.mustache", - clientPackageDir, "IApiAccessor.cs")); - supportingFiles.add(new SupportingFile("Configuration.mustache", - clientPackageDir, "Configuration.cs")); - supportingFiles.add(new SupportingFile("ApiClient.mustache", - clientPackageDir, "ApiClient.cs")); - supportingFiles.add(new SupportingFile("ApiException.mustache", - clientPackageDir, "ApiException.cs")); - supportingFiles.add(new SupportingFile("ApiResponse.mustache", - clientPackageDir, "ApiResponse.cs")); - supportingFiles.add(new SupportingFile("ExceptionFactory.mustache", - clientPackageDir, "ExceptionFactory.cs")); - supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", - clientPackageDir, "OpenAPIDateConverter.cs")); + embeddedTemplateDir = templateDir = "csharp-netcore-functions"; + + // contextually reserved words + // NOTE: C# uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons. + reservedWords.addAll( + Arrays.asList("var", "async", "await", "dynamic", "yield") + ); + + cliOptions.clear(); + + 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("UUID", "Guid"); + typeMapping.put("URI", "string"); + + setSupportNullable(Boolean.TRUE); + + // CLI options + addOption(CodegenConstants.PACKAGE_DESCRIPTION, + CodegenConstants.PACKAGE_DESCRIPTION_DESC, + packageDescription); + + addOption(CodegenConstants.LICENSE_URL, + CodegenConstants.LICENSE_URL_DESC, + licenseUrl); + + addOption(CodegenConstants.LICENSE_NAME, + CodegenConstants.LICENSE_NAME_DESC, + licenseName); + + addOption(CodegenConstants.PACKAGE_COPYRIGHT, + CodegenConstants.PACKAGE_COPYRIGHT_DESC, + packageCopyright); + + addOption(CodegenConstants.PACKAGE_AUTHORS, + CodegenConstants.PACKAGE_AUTHORS_DESC, + packageAuthors); + + addOption(CodegenConstants.PACKAGE_TITLE, + CodegenConstants.PACKAGE_TITLE_DESC, + packageTitle); + + addOption(CodegenConstants.PACKAGE_NAME, + "C# package name (convention: Title.Case).", + packageName); + + addOption(CodegenConstants.PACKAGE_VERSION, + "C# package version.", + packageVersion); + + addOption(CodegenConstants.OPTIONAL_PROJECT_GUID, + CodegenConstants.OPTIONAL_PROJECT_GUID_DESC, + null); + + addOption(CodegenConstants.SOURCE_FOLDER, + CodegenConstants.SOURCE_FOLDER_DESC, + sourceFolder); + + netCoreVersion.addEnum("3.0", ".NET Core 3.0"); + netCoreVersion.addEnum("3.1", ".NET Core 3.1"); + netCoreVersion.addEnum("5.0", ".NET Core 5.0"); + netCoreVersion.addEnum("6.0", ".NET Core 6.0"); + netCoreVersion.setDefault("3.1"); + netCoreVersion.setOptValue(netCoreVersion.getDefault()); + cliOptions.add(netCoreVersion); + + azureFunctionsVersion.addEnum("v4", "Azure Functions v4"); + azureFunctionsVersion.addEnum("v3", "Azure Functions v3"); + azureFunctionsVersion.setDefault("v4"); + azureFunctionsVersion.setOptValue(azureFunctionsVersion.getDefault()); + cliOptions.add(azureFunctionsVersion); + + // CLI Switches + addSwitch(CodegenConstants.NULLABLE_REFERENCE_TYPES, + CodegenConstants.NULLABLE_REFERENCE_TYPES_DESC, + this.nullReferenceTypesFlag); + + addSwitch(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, + CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC, + sortParamsByRequiredFlag); + + addSwitch(CodegenConstants.USE_DATETIME_OFFSET, + CodegenConstants.USE_DATETIME_OFFSET_DESC, + useDateTimeOffsetFlag); + + addSwitch(CodegenConstants.USE_COLLECTION, + CodegenConstants.USE_COLLECTION_DESC, + useCollection); + + addSwitch(CodegenConstants.RETURN_ICOLLECTION, + CodegenConstants.RETURN_ICOLLECTION_DESC, + returnICollection); + + addSwitch(USE_NEWTONSOFT, + "Uses the Newtonsoft JSON library.", + useNewtonsoft); + + addOption(NEWTONSOFT_VERSION, + "Version for Newtonsoft.Json for .NET Core 3.0+", + newtonsoftVersion); + + addOption(CodegenConstants.ENUM_NAME_SUFFIX, + CodegenConstants.ENUM_NAME_SUFFIX_DESC, + enumNameSuffix); + + addOption(CodegenConstants.ENUM_VALUE_SUFFIX, + "Suffix that will be appended to all enum values.", + enumValueSuffix); + + addSwitch(GENERATE_BODY, + "Generates method body.", + generateBody); + + classModifier.addEnum("", "Keep class default with no modifier"); + classModifier.addEnum("abstract", "Make class abstract"); + classModifier.setDefault(""); + classModifier.setOptValue(classModifier.getDefault()); + addOption(classModifier.getOpt(), classModifier.getDescription(), classModifier.getOptValue()); + + operationModifier.addEnum("virtual", "Keep method virtual"); + operationModifier.addEnum("abstract", "Make method abstract"); + operationModifier.setDefault("virtual"); + operationModifier.setOptValue(operationModifier.getDefault()); + cliOptions.add(operationModifier); + + buildTarget.addEnum("program", "Generate code for a standalone server"); + buildTarget.addEnum("library", "Generate code for a server abstract class library"); + buildTarget.setDefault("program"); + buildTarget.setOptValue(buildTarget.getDefault()); + cliOptions.add(buildTarget); + + addSwitch(GENERATE_BODY, + "Generates method body.", + generateBody); + + addSwitch(OPERATION_IS_ASYNC, + "Set methods to async or sync (default).", + operationIsAsync); + + addSwitch(OPERATION_RESULT_TASK, + "Set methods result to Task<>.", + operationResultTask); + + modelClassModifier.setType("String"); + modelClassModifier.addEnum("", "Keep model class default with no modifier"); + modelClassModifier.addEnum("partial", "Make model class partial"); + modelClassModifier.setDefault("partial"); + modelClassModifier.setOptValue(modelClassModifier.getDefault()); + addOption(modelClassModifier.getOpt(), modelClassModifier.getDescription(), modelClassModifier.getOptValue()); + } + + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + @Override + public String getName() { + return "csharp-netcore-functions"; + } + + @Override + public String getHelp() { + return "Generates an ASP.NET Core Web API server."; + } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides()); + additionalProperties.put("serverHost", url.getHost()); + additionalProperties.put("serverPort", URLPathUtils.getPort(url, 8080)); + + setApiBasePath(); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) { + setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID)); + } + additionalProperties.put("packageGuid", packageGuid); + additionalProperties.put("userSecretsGuid", userSecretsGuid); + + if (!additionalProperties.containsKey(NEWTONSOFT_VERSION)) { + additionalProperties.put(NEWTONSOFT_VERSION, newtonsoftVersion); + } else { + newtonsoftVersion = (String) additionalProperties.get(NEWTONSOFT_VERSION); + } + + // Check for the modifiers etc. + // The order of the checks is important. + setClassModifier(); + setOperationModifier(); + setModelClassModifier(); + setOperationIsAsync(); + + + additionalProperties.put("dockerTag", packageName.toLowerCase(Locale.ROOT)); + + if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { + apiPackage = packageName + ".Functions"; + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); + } + + if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { + modelPackage = packageName + ".Models"; + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); + } + + String packageFolder = sourceFolder + File.separator + packageName; + + // determine the ASP.NET core version setting + setAzureFunctionsVersion(); + setNetCoreVersion(packageFolder); + setUseNewtonsoft(); + + // Check for class modifier if not present set the default value. + + supportingFiles.add(new SupportingFile("build.sh.mustache", "", "build.sh")); + supportingFiles.add(new SupportingFile("build.bat.mustache", "", "build.bat")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); + supportingFiles.add(new SupportingFile("gitignore", packageFolder, ".gitignore")); + supportingFiles.add(new SupportingFile("OpenApi" + File.separator + "TypeExtensions.mustache", packageFolder + File.separator + "OpenApi", "TypeExtensions.cs")); + supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, packageName + ".csproj")); + supportingFiles.add(new SupportingFile("typeConverter.mustache", packageFolder + File.separator + "Converters", "CustomEnumConverter.cs")); + supportingFiles.add(new SupportingFile("host.json.mustache", packageFolder, "host.json")); + supportingFiles.add(new SupportingFile("local.settings.json.mustache", packageFolder, "local.settings.json")); + } + + public void setPackageGuid(String packageGuid) { + this.packageGuid = packageGuid; } @Override public String apiFileFolder() { - return outputFolder + File.separator + "generatedSrc" + File.separator + "Functions"; + return outputFolder + File.separator + sourceFolder + File.separator + packageName + File.separator + "Functions"; } @Override public String modelFileFolder() { - return outputFolder + File.separator + "generatedSrc" + File.separator + "Models"; + return outputFolder + File.separator + sourceFolder + File.separator + packageName + File.separator + "Models"; } @Override - public String apiDocFileFolder() { - return (outputFolder + "/Docs").replace('/', File.separatorChar); + public Map postProcessSupportingFileData(Map objs) { + generateJSONSpecFile(objs); + return super.postProcessSupportingFileData(objs); } @Override - public String apiTestFileFolder() { - return outputFolder + File.separator + "Tests" + File.separator + "Tests" + File.separator + apiPackage(); + protected void processOperation(CodegenOperation operation) { + super.processOperation(operation); + + // HACK: Unlikely in the wild, but we need to clean operation paths for MVC Routing + if (operation.path != null) { + if (operation.path.startsWith("/")) + { + operation.path = operation.path.substring(1); + } + String original = operation.path; + operation.path = operation.path.replace("?", "/"); + if (!original.equals(operation.path)) { + LOGGER.warn("Normalized {} to {}. Please verify generated source.", original, operation.path); + } + } + + // Converts, for example, PUT to HttpPut for function attributes + operation.httpMethod = operation.httpMethod.charAt(0) + operation.httpMethod.substring(1).toLowerCase(Locale.ROOT); } + @Override + public Map postProcessOperationsWithModels(Map objs, List allModels) { + super.postProcessOperationsWithModels(objs, allModels); + // We need to postprocess the operations to add proper consumes tags and fix form file handling + if (objs != null) { + Map operations = (Map) objs.get("operations"); + if (operations != null) { + List ops = (List) operations.get("operation"); + for (CodegenOperation operation : ops) { + if (operation.consumes == null) { + continue; + } + if (operation.consumes.size() == 0) { + continue; + } + + // Build a consumes string for the operation we cannot iterate in the template as we need a ',' + // after each entry but the last + + StringBuilder consumesString = new StringBuilder(); + for (Map consume : operation.consumes) { + if (!consume.containsKey("mediaType")) { + continue; + } + + if(consumesString.toString().isEmpty()) { + consumesString = new StringBuilder("\"" + consume.get("mediaType") + "\""); + } + else { + consumesString.append(", \"").append(consume.get("mediaType")).append("\""); + } + + // In a multipart/form-data consuming context binary data is best handled by an IFormFile + if (!consume.get("mediaType").equals("multipart/form-data")) { + continue; + } + + // Change dataType of binary parameters to IFormFile for formParams in multipart/form-data + for (CodegenParameter param : operation.formParams) { + if (param.isBinary) { + param.dataType = "IFormFile"; + param.baseType = "IFormFile"; + } + } + + for (CodegenParameter param : operation.allParams) { + if (param.isBinary && param.isFormParam) { + param.dataType = "IFormFile"; + param.baseType = "IFormFile"; + } + } + } + + if(!consumesString.toString().isEmpty()) { + operation.vendorExtensions.put("x-aspnetcore-consumes", consumesString.toString()); + } + } + } + } + return objs; + } + + @Override + public Mustache.Compiler processCompiler(Mustache.Compiler compiler) { + // To avoid unexpected behaviors when options are passed programmatically such as { "useCollection": "" } + return super.processCompiler(compiler).emptyStringIsFalse(true); + } + + @Override + public String toRegularExpression(String pattern) { + return escapeText(pattern); + } + + @SuppressWarnings("rawtypes") + @Override + public String getNullableType(Schema p, String type) { + if (languageSpecificPrimitives.contains(type)) { + if (isSupportNullable() && ModelUtils.isNullable(p) && (nullableType.contains(type) || nullReferenceTypesFlag)) { + return type + "?"; + } else { + return type; + } + } else { + return null; + } + } + + private void setCliOption(CliOption cliOption) throws IllegalArgumentException { + if (additionalProperties.containsKey(cliOption.getOpt())) { + // TODO Hack - not sure why the empty strings become boolean. + Object obj = additionalProperties.get(cliOption.getOpt()); + if (!SchemaTypeUtil.BOOLEAN_TYPE.equals(cliOption.getType())) { + if (obj instanceof Boolean) { + obj = ""; + additionalProperties.put(cliOption.getOpt(), obj); + } + } + cliOption.setOptValue(obj.toString()); + } else { + additionalProperties.put(cliOption.getOpt(), cliOption.getOptValue()); + } + if (cliOption.getOptValue() == null) { + cliOption.setOptValue(cliOption.getDefault()); + throw new IllegalArgumentException(cliOption.getOpt() + ": Invalid value '" + additionalProperties.get(cliOption.getOpt()).toString() + "'" + + ". " + cliOption.getDescription()); + } + } + + private void setClassModifier() { + // CHeck for class modifier if not present set the default value. + setCliOption(classModifier); + + // If class modifier is abstract then the methods need to be abstract too. + if ("abstract".equals(classModifier.getOptValue())) { + operationModifier.setOptValue(classModifier.getOptValue()); + additionalProperties.put(OPERATION_MODIFIER, operationModifier.getOptValue()); + LOGGER.warn("classModifier is {} so forcing operationModifier to {}", classModifier.getOptValue(), operationModifier.getOptValue()); + } + } + + private void setOperationModifier() { + setCliOption(operationModifier); + + // If operation modifier is abstract then dont generate any body + if ("abstract".equals(operationModifier.getOptValue())) { + generateBody = false; + additionalProperties.put(GENERATE_BODY, generateBody); + LOGGER.warn("operationModifier is {} so forcing generateBody to {}", operationModifier.getOptValue(), generateBody); + } else if (additionalProperties.containsKey(GENERATE_BODY)) { + generateBody = convertPropertyToBooleanAndWriteBack(GENERATE_BODY); + } else { + additionalProperties.put(GENERATE_BODY, generateBody); + } + } + + private void setModelClassModifier() { + setCliOption(modelClassModifier); + + // If operation modifier is abstract then dont generate any body + if (isLibrary) { + modelClassModifier.setOptValue(""); + additionalProperties.put(MODEL_CLASS_MODIFIER, modelClassModifier.getOptValue()); + LOGGER.warn("buildTarget is {} so removing any modelClassModifier ", buildTarget.getOptValue()); + } + } + + private void setNetCoreVersion(String packageFolder) { + setCliOption(netCoreVersion); + + LOGGER.info("ASP.NET core version: {}", netCoreVersion.getOptValue()); + } + + private void setAzureFunctionsVersion() { + setCliOption(azureFunctionsVersion); + String functionsSDKVersion = "3.0.13"; + + if ("v4".equals(azureFunctionsVersion.getOptValue())) { + functionsSDKVersion = "4.0.1"; + + if (!netCoreVersion.getOptValue().startsWith("6.")) { + LOGGER.warn("ASP.NET core version: {} is not compatible with Azure functions v4. Using version 6.0.", netCoreVersion.getOptValue()); + netCoreVersion.setOptValue("6.0"); + } + } + + additionalProperties.put(FUNCTIONS_SDK_VERSION, functionsSDKVersion); + + //set .NET target version + String targetFrameworkVersion = "net" + netCoreVersion.getOptValue(); + additionalProperties.put(TARGET_FRAMEWORK, targetFrameworkVersion); + } + + private void setOperationIsAsync() { + if (isLibrary) { + operationIsAsync = false; + additionalProperties.put(OPERATION_IS_ASYNC, operationIsAsync); + } else if (additionalProperties.containsKey(OPERATION_IS_ASYNC)) { + operationIsAsync = convertPropertyToBooleanAndWriteBack(OPERATION_IS_ASYNC); + } else { + additionalProperties.put(OPERATION_IS_ASYNC, operationIsAsync); + } + } + + private void setUseNewtonsoft() { + if (additionalProperties.containsKey(USE_NEWTONSOFT)) { + useNewtonsoft = convertPropertyToBooleanAndWriteBack(USE_NEWTONSOFT); + } else { + additionalProperties.put(USE_NEWTONSOFT, useNewtonsoft); + } + } + + private void setApiBasePath() { + URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides()); + String apiBasePath = encodePath(url.getPath()).replaceAll("/$", ""); + + // if a base path exists, remove leading '/' and append trailing '/' + if (apiBasePath != null && apiBasePath.length() > 0) { + if (apiBasePath.startsWith("/")) + apiBasePath = apiBasePath.substring(1); + + if (apiBasePath.endsWith("/")) + apiBasePath = apiBasePath.substring(0, apiBasePath.lastIndexOf("/")); + } + + additionalProperties.put("apiBasePath", apiBasePath); + } } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenApi/TypeExtensions.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenApi/TypeExtensions.mustache new file mode 100644 index 00000000000..df00a93e8c6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenApi/TypeExtensions.mustache @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Text; + +namespace {{packageName}}.OpenApi +{ + /// + /// Replacement utilities from Swashbuckle.AspNetCore.SwaggerGen which are not in 5.x + /// + public static class TypeExtensions + { + /// + /// Produce a friendly name for the type which is unique. + /// + /// + /// + public static string FriendlyId(this Type type, bool fullyQualified = false) + { + var typeName = fullyQualified + ? type.FullNameSansTypeParameters().Replace("+", ".") + : type.Name; + + if (type.IsGenericType) + { + var genericArgumentIds = type.GetGenericArguments() + .Select(t => t.FriendlyId(fullyQualified)) + .ToArray(); + + return new StringBuilder(typeName) + .Replace($"`{genericArgumentIds.Count()}", string.Empty) + .Append($"[{string.Join(",", genericArgumentIds).TrimEnd(',')}]") + .ToString(); + } + + return typeName; + } + + /// + /// Determine the fully qualified type name without type parameters. + /// + /// + public static string FullNameSansTypeParameters(this Type type) + { + var fullName = type.FullName; + if (string.IsNullOrEmpty(fullName)) + fullName = type.Name; + var chopIndex = fullName.IndexOf("[[", StringComparison.Ordinal); + return (chopIndex == -1) ? fullName : fullName.Substring(0, chopIndex); + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache new file mode 100644 index 00000000000..54ebdc16166 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache @@ -0,0 +1,39 @@ + + + {{packageDescription}}{{^packageDescription}}{{packageName}}{{/packageDescription}} + {{packageCopyright}} + {{packageAuthors}} + {{targetFramework}} + true + true + {{packageVersion}} + {{azureFunctionsVersion}} +{{#nullableReferenceTypes}} + annotations +{{/nullableReferenceTypes}} +{{#isLibrary}} + Library +{{/isLibrary}} + {{packageName}} + {{packageName}} + {{userSecretsGuid}} + Linux + ..\.. + + + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache.bak b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache.bak new file mode 100644 index 00000000000..ab4ad28ede6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.csproj.mustache.bak @@ -0,0 +1,36 @@ + + + {{packageDescription}}{{^packageDescription}}{{packageName}}{{/packageDescription}} + {{packageCopyright}} + {{packageAuthors}} + {{targetFramework}} + true + true + {{packageVersion}} + {{azureFunctionsVersion}} +{{#nullableReferenceTypes}} + annotations +{{/nullableReferenceTypes}} +{{#isLibrary}} + Library +{{/isLibrary}} + {{packageName}} + {{packageName}} + {{userSecretsGuid}} + Linux + ..\.. + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.nuspec.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.nuspec.mustache new file mode 100644 index 00000000000..1d24d331642 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Project.nuspec.mustache @@ -0,0 +1,20 @@ + + + + $id$ + {{packageVersion}} + {{packageTitle}} + {{packageAuthors}} + {{packageAuthors}} + {{licenseUrl}} + + false + {{packageDescription}}{{^packageDescription}}{{packageName}}{{/packageDescription}} + Summary of changes made in this release of the package. + {{packageCopyright}} + {{packageName}} + + diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/README.mustache index b15d2a25bf1..0fda2bdb3be 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/README.mustache @@ -1,270 +1,28 @@ -# {{packageName}} - the C# library for the {{appName}} +# {{packageName}} - Azure Functions {{azureFunctionsVersion}} Server {{#appDescriptionWithNewLines}} -{{{appDescriptionWithNewLines}}} +{{{.}}} {{/appDescriptionWithNewLines}} -This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: +## Run -- API version: {{appVersion}} -- SDK version: {{packageVersion}} -{{^hideGenerationTimestamp}} -- Build date: {{generatedDate}} -{{/hideGenerationTimestamp}} -- Build package: {{generatorClass}} -{{#infoUrl}} - For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) -{{/infoUrl}} - - -## Frameworks supported -{{#netStandard}} -- .NET Core >=1.0 -- .NET Framework >=4.6 -- Mono/Xamarin >=vNext -{{/netStandard}} - - -## Dependencies - -{{#useRestSharp}} -- [RestSharp](https://www.nuget.org/packages/RestSharp) - 106.11.7 or later -{{/useRestSharp}} -- [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) - 12.0.3 or later -- [JsonSubTypes](https://www.nuget.org/packages/JsonSubTypes/) - 1.8.0 or later -{{#useCompareNetObjects}} -- [CompareNETObjects](https://www.nuget.org/packages/CompareNETObjects) - 4.61.0 or later -{{/useCompareNetObjects}} -{{#validatable}} -- [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 5.0.0 or later -{{/validatable}} - -The DLLs included in the package may not be the latest version. We recommend using [NuGet](https://docs.nuget.org/consume/installing-nuget) to obtain the latest version of the packages: -``` -{{#useRestSharp}} -Install-Package RestSharp -{{/useRestSharp}} -Install-Package Newtonsoft.Json -Install-Package JsonSubTypes -{{#validatable}} -Install-Package System.ComponentModel.Annotations -{{/validatable}} -{{#useCompareNetObjects}} -Install-Package CompareNETObjects -{{/useCompareNetObjects}} -``` -{{#useRestSharp}} - -NOTE: RestSharp versions greater than 105.1.0 have a bug which causes file uploads to fail. See [RestSharp#742](https://github.com/restsharp/RestSharp/issues/742). -NOTE: RestSharp for .Net Core creates a new socket for each api call, which can lead to a socket exhaustion problem. See [RestSharp#1406](https://github.com/restsharp/RestSharp/issues/1406). - -{{/useRestSharp}} - -## Installation -{{#netStandard}} -Generate the DLL using your preferred tool (e.g. `dotnet build`) -{{/netStandard}} -{{^netStandard}} -Run the following command to generate the DLL -- [Mac/Linux] `/bin/sh build.sh` -- [Windows] `build.bat` -{{/netStandard}} - -Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces: -```csharp -using {{packageName}}.{{apiPackage}}; -using {{packageName}}.Client; -using {{packageName}}.{{modelPackage}}; -``` -{{^netStandard}} - -## Packaging - -A `.nuspec` is included with the project. You can follow the Nuget quickstart to [create](https://docs.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package#create-the-package) and [publish](https://docs.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package#publish-the-package) packages. - -This `.nuspec` uses placeholders from the `.csproj`, so build the `.csproj` directly: +Linux/OS X: ``` -nuget pack -Build -OutputDirectory out {{packageName}}.csproj +sh build.sh ``` -Then, publish to a [local feed](https://docs.microsoft.com/en-us/nuget/hosting-packages/local-feeds) or [other host](https://docs.microsoft.com/en-us/nuget/hosting-packages/overview) and consume the new package via Nuget as usual. +Windows: -{{/netStandard}} - -## Usage - -To use the API client with a HTTP proxy, setup a `System.Net.WebProxy` -```csharp -Configuration c = new Configuration(); -System.Net.WebProxy webProxy = new System.Net.WebProxy("http://myProxyUrl:80/"); -webProxy.Credentials = System.Net.CredentialCache.DefaultCredentials; -c.Proxy = webProxy; ``` -{{#useHttpClient}} - -### Connections -Each ApiClass (properly the ApiClient inside it) will create an istance of HttpClient. It will use that for the entire lifecycle and dispose it when called the Dispose method. - -To better manager the connections it's a common practice to reuse the HttpClient and HttpClientHander (see [here](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net) for details). To use your own HttpClient instance just pass it to the ApiClass constructor. - -```csharp -HttpClientHandler yourHandler = new HttpClientHandler(); -HttpClient yourHttpClient = new HttpClient(yourHandler); -var api = new YourApiClass(yourHttpClient, yourHandler); +build.bat ``` +{{^isLibrary}} +## Run in Docker -If you want to use an HttpClient and don't have access to the handler, for example in a DI context in Asp.net Core when using IHttpClientFactory. - -```csharp -HttpClient yourHttpClient = new HttpClient(); -var api = new YourApiClass(yourHttpClient); ``` -You'll loose some configuration settings, the features affected are: Setting and Retrieving Cookies, Client Certificates, Proxy settings. You need to either manually handle those in your setup of the HttpClient or they won't be available. - -Here an example of DI setup in a sample web project: - -```csharp -services.AddHttpClient(httpClient => - new PetApi(httpClient)); +cd {{sourceFolder}}/{{packageName}} +docker build -t {{dockerTag}} . +docker run -p 5000:8080 {{dockerTag}} ``` - -{{/useHttpClient}} - - -## Getting Started - -```csharp -using System.Collections.Generic; -using System.Diagnostics; -{{#useHttpClient}} -using System.Net.Http; -{{/useHttpClient}} -using {{packageName}}.{{apiPackage}}; -using {{packageName}}.Client; -using {{packageName}}.{{modelPackage}}; - -namespace Example -{ - public class {{operationId}}Example - { - public static void Main() - { -{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}} - Configuration config = new Configuration(); - config.BasePath = "{{{basePath}}}"; - {{#hasAuthMethods}} - {{#authMethods}} - {{#isBasicBasic}} - // Configure HTTP basic authorization: {{{name}}} - config.Username = "YOUR_USERNAME"; - config.Password = "YOUR_PASSWORD"; - {{/isBasicBasic}} - {{#isBasicBearer}} - // Configure Bearer token for authorization: {{{name}}} - config.AccessToken = "YOUR_BEARER_TOKEN"; - {{/isBasicBearer}} - {{#isApiKey}} - // Configure API key authorization: {{{name}}} - config.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); - // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // config.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer"); - {{/isApiKey}} - {{#isOAuth}} - // Configure OAuth2 access token for authorization: {{{name}}} - config.AccessToken = "YOUR_ACCESS_TOKEN"; - {{/isOAuth}} - {{/authMethods}} - - {{/hasAuthMethods}} - {{#useHttpClient}} - // create instances of HttpClient, HttpClientHandler to be reused later with different Api classes - HttpClient httpClient = new HttpClient(); - HttpClientHandler httpClientHandler = new HttpClientHandler(); - var apiInstance = new {{classname}}(httpClient, config, httpClientHandler); - {{/useHttpClient}} - {{^useHttpClient}} - var apiInstance = new {{classname}}(config); - {{/useHttpClient}} - {{#allParams}} - {{#isPrimitiveType}} - var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{^isPrimitiveType}} - var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{/allParams}} - - try - { - {{#summary}} - // {{{.}}} - {{/summary}} - {{#returnType}}{{{returnType}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}} - Debug.WriteLine(result);{{/returnType}} - } - catch (ApiException e) - { - Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); - Debug.Print("Status Code: "+ e.ErrorCode); - Debug.Print(e.StackTrace); - } -{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} - } - } -} -``` - - -## Documentation for API Endpoints - -All URIs are relative to *{{{basePath}}}* - -Class | Method | HTTP request | Description ------------- | ------------- | ------------- | ------------- -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} -{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} - - -## Documentation for Models - -{{#modelPackage}} -{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) -{{/model}}{{/models}} -{{/modelPackage}} -{{^modelPackage}} -No model defined in this package -{{/modelPackage}} - - -## Documentation for Authorization - -{{^authMethods}} -All endpoints do not require authorization. -{{/authMethods}} -{{#authMethods}} -{{#last}} -Authentication schemes defined for the API: -{{/last}} -{{/authMethods}} -{{#authMethods}} - -### {{name}} - -{{#isApiKey}}- **Type**: API key -- **API key parameter name**: {{keyParamName}} -- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} -{{/isApiKey}} -{{#isBasicBasic}}- **Type**: HTTP basic authentication -{{/isBasicBasic}} -{{#isBasicBearer}}- **Type**: Bearer Authentication -{{/isBasicBearer}} -{{#isOAuth}}- **Type**: OAuth -- **Flow**: {{flow}} -- **Authorization URL**: {{authorizationUrl}} -- **Scopes**: {{^scopes}}N/A{{/scopes}} -{{#scopes}} - {{scope}}: {{description}} -{{/scopes}} -{{/isOAuth}} - -{{/authMethods}} +{{/isLibrary}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Solution.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Solution.mustache index 112cc3dc405..8c6d69ea93d 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Solution.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Solution.mustache @@ -1,27 +1,22 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio {{^netStandard}}2012{{/netStandard}}{{#netStandard}}14{{/netStandard}} -VisualStudioVersion = {{^netStandard}}12.0.0.0{{/netStandard}}{{#netStandard}}14.0.25420.1{{/netStandard}} -MinimumVisualStudioVersion = {{^netStandard}}10.0.0.1{{/netStandard}}{{#netStandard}}10.0.40219.1{{/netStandard}} -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{{packageName}}", "src\{{packageName}}\{{packageName}}.csproj", "{{packageGuid}}" +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2043 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{{packageName}}", "{{sourceFolder}}\{{packageName}}\{{packageName}}.csproj", "{{packageGuid}}" EndProject -{{^excludeTests}}Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{{testPackageName}}", "src\{{testPackageName}}\{{testPackageName}}.csproj", "{19F1DEBC-DE5E-4517-8062-F000CD499087}" -EndProject -{{/excludeTests}}Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {{packageGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {{packageGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU - {{packageGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU - {{packageGuid}}.Release|Any CPU.Build.0 = Release|Any CPU - {19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.Build.0 = Debug|Any CPU - {19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.ActiveCfg = Release|Any CPU - {19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal \ No newline at end of file +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {{packageGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {{packageGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU + {{packageGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU + {{packageGuid}}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.Development.json b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.Development.json new file mode 100644 index 00000000000..e203e9407e7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.json b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.json new file mode 100644 index 00000000000..def9159a7d9 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/bodyParam.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/bodyParam.mustache new file mode 100644 index 00000000000..02b0fa1d2de --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/bodyParam.mustache @@ -0,0 +1 @@ +{{#isBodyParam}}[FromBody]{{&dataType}} {{paramName}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.bat.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.bat.mustache new file mode 100644 index 00000000000..e437bccf7d6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.bat.mustache @@ -0,0 +1,9 @@ +:: Generated by: https://openapi-generator.tech +:: + +@echo off + +dotnet restore {{sourceFolder}}\{{packageName}} +dotnet build {{sourceFolder}}\{{packageName}} +echo Now, run the following to start the project: dotnet run -p {{sourceFolder}}\{{packageName}}\{{packageName}}.csproj --launch-profile web. +echo. diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.sh.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.sh.mustache new file mode 100644 index 00000000000..356d6637be1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/build.sh.mustache @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# +# Generated by: https://openapi-generator.tech +# + +dotnet restore {{sourceFolder}}/{{packageName}}/ && \ + dotnet build {{sourceFolder}}/{{packageName}}/ && \ + echo "Now, run the following to start the project: func start" diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/controller.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/controller.mustache new file mode 100644 index 00000000000..ba1d43982a7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/controller.mustache @@ -0,0 +1,88 @@ +{{>partial_header}} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +{{#operationResultTask}} +using System.Threading.Tasks; +{{/operationResultTask}} +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +{{#useSwashbuckle}} +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.SwaggerGen; +{{/useSwashbuckle}} +{{^isLibrary}} +using Newtonsoft.Json; +{{/isLibrary}} +using {{packageName}}.Attributes; +using {{modelPackage}}; + +namespace {{apiPackage}} +{ {{#operations}} + /// + /// {{description}} + /// {{#description}} + [Description("{{.}}")]{{/description}} + [ApiController] + public {{#classModifier}}{{.}} {{/classModifier}}class {{classname}}Controller : ControllerBase + { {{#operation}} + /// + /// {{summary}} + /// {{#notes}} + /// {{.}}{{/notes}}{{#allParams}} + /// {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}}{{/allParams}}{{#responses}} + /// {{message}}{{/responses}} + [{{httpMethod}}] + [Route("{{{basePathWithoutHost}}}{{{path}}}")] +{{#authMethods}} +{{#isApiKey}} + [Authorize(Policy = "{{name}}")] +{{/isApiKey}} +{{#isBasicBearer}} + [Authorize{{#scopes}}{{#-first}}(Roles = "{{/-first}}{{scope}}{{^-last}},{{/-last}}{{#-last}}"){{/-last}}{{/scopes}}] +{{/isBasicBearer}} +{{/authMethods}} + {{#vendorExtensions.x-aspnetcore-consumes}} + [Consumes({{&vendorExtensions.x-aspnetcore-consumes}})] + {{/vendorExtensions.x-aspnetcore-consumes}} + [ValidateModelState]{{#useSwashbuckle}} + [SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}} + [SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{/responses}}{{/useSwashbuckle}}{{^useSwashbuckle}}{{#responses}}{{#dataType}} + [ProducesResponseType(statusCode: {{code}}, type: typeof({{&dataType}}))]{{/dataType}}{{/responses}}{{/useSwashbuckle}} + {{#isDeprecated}} + [Obsolete] + {{/isDeprecated}} + public {{operationModifier}} {{#operationResultTask}}{{#operationIsAsync}}async {{/operationIsAsync}}Task<{{/operationResultTask}}IActionResult{{#operationResultTask}}>{{/operationResultTask}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{^-last}}{{^isCookieParam}}, {{/isCookieParam}}{{/-last}}{{/allParams}}){{^generateBody}};{{/generateBody}} + {{#generateBody}} + { + {{#cookieParams}} + var {{paramName}} = Request.Cookies["{{paramName}}"]; + {{/cookieParams}} + +{{#responses}} +{{#dataType}} + //TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ... + // return StatusCode({{code}}, default({{&dataType}})); +{{/dataType}} +{{^dataType}} + //TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ... + // return StatusCode({{code}}); +{{/dataType}} +{{/responses}} +{{#returnType}} + string exampleJson = null; + {{#examples}} + exampleJson = "{{{example}}}"; + {{/examples}} + {{#isListCollection}}{{>listReturn}}{{/isListCollection}}{{^isListCollection}}{{#isMap}}{{>mapReturn}}{{/isMap}}{{^isMap}}{{>objectReturn}}{{/isMap}}{{/isListCollection}} + {{!TODO: defaultResponse, examples, auth, consumes, produces, nickname, externalDocs, imports, security}} + //TODO: Change the data returned + return {{#operationResultTask}}Task.FromResult({{/operationResultTask}}new ObjectResult(example){{#operationResultTask}}){{/operationResultTask}};{{/returnType}}{{^returnType}} + throw new NotImplementedException();{{/returnType}} + } + {{/generateBody}} + {{/operation}} + } +{{/operations}} +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/enumClass.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/enumClass.mustache new file mode 100644 index 00000000000..cd6595452ae --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/enumClass.mustache @@ -0,0 +1,19 @@ + + /// + /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}} + /// + {{#description}} + /// {{{.}}} + {{/description}} + {{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}[TypeConverter(typeof(CustomEnumConverter<{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}} + public enum {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} + { + {{#allowableValues}}{{#enumVars}} + /// + /// Enum {{name}} for {{{value}}} + /// + {{#isString}}[EnumMember(Value = "{{{value}}}")]{{/isString}} + {{name}}{{^isString}} = {{{value}}}{{/isString}}{{#isString}} = {{-index}}{{/isString}}{{^-last}}, + {{/-last}}{{/enumVars}}{{/allowableValues}} + } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/formParam.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/formParam.mustache new file mode 100644 index 00000000000..d22f70b0e26 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/formParam.mustache @@ -0,0 +1 @@ +{{#isFormParam}}{{^isBinary}}[FromForm{{^isModel}} (Name = "{{baseName}}"){{/isModel}}]{{/isBinary}}{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{.}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}})]{{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{.}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache new file mode 100644 index 00000000000..76d12702820 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache @@ -0,0 +1,33 @@ +using System.IO; +using System.Net; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json; + +namespace {{apiPackage}} +{ {{#operations}} + public partial {{#classModifier}}{{classModifier}} {{/classModifier}}class {{classname}} + { {{#operation}} + [FunctionName("{{classname}}_{{operationId}}")] + public async Task _{{operationId}}([HttpTrigger(AuthorizationLevel.Anonymous, "{{httpMethod}}", Route = "{{{apiBasePath}}}{{{path}}}")]HttpRequest req, ExecutionContext context{{#allParams}}{{#isPathParam}}, {{>pathParam}}{{/isPathParam}}{{/allParams}}){{^generateBody}};{{/generateBody}} + {{#generateBody}} + { + var method = this.GetType().GetMethod("{{operationId}}"); + + return method != null + ? (await ((Task)method.Invoke(this, new object[] { req, context{{#allParams}}{{#isPathParam}}, {{>paramName}}{{/isPathParam}}{{/allParams}} })).ConfigureAwait(false)) + : new StatusCodeResult((int)HttpStatusCode.NotImplemented); + } + {{/generateBody}} + {{/operation}} + } +{{/operations}} +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache.bak b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache.bak new file mode 100644 index 00000000000..397ba84eb77 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/function.mustache.bak @@ -0,0 +1,33 @@ +using System.IO; +using System.Net; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json; + +namespace {{apiPackage}} +{ {{#operations}} + public partial {{#classModifier}}{{classModifier}} {{/classModifier}}class {{classname}} + { {{#operation}} + [FunctionName("{{classname}}_{{operationId}}")] + public async Task _{{operationId}}([HttpTrigger(AuthorizationLevel.Anonymous, "{{httpMethod}}", Route = "{{{path}}}")]HttpRequest req, ExecutionContext context{{#allParams}}{{#isPathParam}}, {{>pathParam}}{{/isPathParam}}{{/allParams}}){{^generateBody}};{{/generateBody}} + {{#generateBody}} + { + var method = this.GetType().GetMethod("{{operationId}}"); + + return method != null + ? (await ((Task)method.Invoke(this, new object[] { req, context{{#allParams}}{{#isPathParam}}, {{>paramName}}{{/isPathParam}}{{/allParams}} })).ConfigureAwait(false)) + : new StatusCodeResult((int)HttpStatusCode.NotImplemented); + } + {{/generateBody}} + {{/operation}} + } +{{/operations}} +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore new file mode 100644 index 00000000000..1ee53850b84 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/headerParam.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/headerParam.mustache new file mode 100644 index 00000000000..a742a222236 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/headerParam.mustache @@ -0,0 +1 @@ +{{#isHeaderParam}}[FromHeader]{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{.}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}})]{{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{.}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/host.json.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/host.json.mustache new file mode 100644 index 00000000000..beb2e4020b8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/host.json.mustache @@ -0,0 +1,11 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/listReturn.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/listReturn.mustache new file mode 100644 index 00000000000..9f18d71d04c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/listReturn.mustache @@ -0,0 +1,4 @@ + + var example = exampleJson != null + ? JsonConvert.DeserializeObject<{{returnContainer}}<{{{returnType}}}>>(exampleJson) + : Enumerable.Empty<{{{returnType}}}>(); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/local.settings.json.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/local.settings.json.mustache new file mode 100644 index 00000000000..4fce9ff39ed --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/local.settings.json.mustache @@ -0,0 +1,7 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet" + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/mapReturn.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/mapReturn.mustache new file mode 100644 index 00000000000..94f16e11fdc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/mapReturn.mustache @@ -0,0 +1,4 @@ + + var example = exampleJson != null + ? JsonConvert.DeserializeObject>(exampleJson) + : new Dictionary<{{{returnType}}}>(); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/model.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/model.mustache index 339a6d2f76b..eebb50bf87c 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/model.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/model.mustache @@ -1,47 +1,160 @@ {{>partial_header}} - using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -using System.IO; -using System.Runtime.Serialization; using System.Text; -using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; +using {{packageName}}.Converters; {{#models}} {{#model}} -{{#discriminator}} -using JsonSubTypes; -{{/discriminator}} {{/model}} {{/models}} -{{#validatable}} -using System.ComponentModel.DataAnnotations; -{{/validatable}} -using OpenAPIDateConverter = {{packageName}}.Client.OpenAPIDateConverter; -{{#useCompareNetObjects}} -using OpenAPIClientUtils = {{packageName}}.Client.ClientUtils; -{{/useCompareNetObjects}} + {{#models}} {{#model}} -{{#oneOf}} -{{#-first}} -using System.Reflection; -{{/-first}} -{{/oneOf}} -{{#aneOf}} -{{#-first}} -using System.Reflection; -{{/-first}} -{{/aneOf}} +namespace {{modelPackage}} +{ {{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}} + /// + /// {{description}} + /// + [DataContract] + public {{#modelClassModifier}}{{.}} {{/modelClassModifier}}class {{classname}} : {{#parent}}{{{.}}}, {{/parent}}IEquatable<{{classname}}> + { + {{#vars}} + {{#items.isEnum}} + {{#items}} + {{^complexType}} +{{>enumClass}} + {{/complexType}} + {{/items}} + {{/items.isEnum}} + {{^items.isEnum}} + {{#isEnum}} + {{^complexType}} +{{>enumClass}} + {{/complexType}} + {{/isEnum}} + {{/items.isEnum}} + /// + /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + /// {{#description}} + /// {{.}}{{/description}}{{#required}} + [Required]{{/required}}{{#pattern}} + [RegularExpression("{{{.}}}")]{{/pattern}}{{#minLength}}{{#maxLength}} + [StringLength({{maxLength}}, MinimumLength={{minLength}})]{{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} + [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} + [MaxLength({{.}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}} + [Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}} + [DataMember(Name="{{baseName}}", EmitDefaultValue={{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}})] + {{#isEnum}} + public {{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}} {{name}} { get; set; }{{#defaultValue}} = {{{.}}};{{/defaultValue}} + {{/isEnum}} + {{^isEnum}} + public {{{dataType}}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; }{{#defaultValue}} = {{{.}}};{{/defaultValue}} + {{/isEnum}} + {{^-last}} -namespace {{packageName}}.{{modelPackage}} -{ -{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{#anyOf}}{{#-first}}{{>modelAnyOf}}{{/-first}}{{/anyOf}}{{^oneOf}}{{^anyOf}}{{>modelGeneric}}{{/anyOf}}{{/oneOf}}{{/isEnum}} + {{/-last}} + {{/vars}} + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append("class {{classname}} {\n"); + {{#vars}} + sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); + {{/vars}} + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public {{#parent}}{{^isMap}}{{^isArray}}new {{/isArray}}{{/isMap}}{{/parent}}string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals(({{classname}})obj); + } + + /// + /// Returns true if {{classname}} instances are equal + /// + /// Instance of {{classname}} to be compared + /// Boolean + public bool Equals({{classname}} other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + + return {{#vars}}{{^isContainer}} + ( + {{name}} == other.{{name}} || + {{^vendorExtensions.x-is-value-type}}{{name}} != null &&{{/vendorExtensions.x-is-value-type}} + {{name}}.Equals(other.{{name}}) + ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}} + ( + {{name}} == other.{{name}} || + {{^vendorExtensions.x-is-value-type}}{{name}} != null && + other.{{name}} != null && + {{/vendorExtensions.x-is-value-type}}{{name}}.SequenceEqual(other.{{name}}) + ){{^-last}} && {{/-last}}{{/isContainer}}{{/vars}}{{^vars}}false{{/vars}}; + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + var hashCode = 41; + // Suitable nullity checks etc, of course :) + {{#vars}} + {{^vendorExtensions.x-is-value-type}}if ({{name}} != null){{/vendorExtensions.x-is-value-type}} + hashCode = hashCode * 59 + {{name}}.GetHashCode(); + {{/vars}} + return hashCode; + } + } + + #region Operators + #pragma warning disable 1591 + + public static bool operator ==({{classname}} left, {{classname}} right) + { + return Equals(left, right); + } + + public static bool operator !=({{classname}} left, {{classname}} right) + { + return !Equals(left, right); + } + + #pragma warning restore 1591 + #endregion Operators + } +{{/isEnum}} {{/model}} {{/models}} } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/objectReturn.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/objectReturn.mustache new file mode 100644 index 00000000000..b037a14ceba --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/objectReturn.mustache @@ -0,0 +1,4 @@ + + var example = exampleJson != null + ? JsonConvert.DeserializeObject<{{{returnType}}}>(exampleJson) + : default({{{returnType}}}); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/paramName.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/paramName.mustache new file mode 100644 index 00000000000..1adee60e5f8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/paramName.mustache @@ -0,0 +1 @@ +{{paramName}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/partial_header.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/partial_header.mustache index 755e937d23b..408d841df26 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/partial_header.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/partial_header.mustache @@ -1,17 +1,13 @@ /* {{#appName}} - * {{{appName}}} + * {{{.}}} * {{/appName}} {{#appDescription}} - * {{{appDescription}}} + * {{{.}}} * {{/appDescription}} - {{#version}} - * The version of the OpenAPI document: {{{version}}} - {{/version}} - {{#infoEmail}} - * Contact: {{{infoEmail}}} - {{/infoEmail}} - * Generated by: https://github.com/openapitools/openapi-generator.git + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * Generated by: https://openapi-generator.tech */ diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/queryParam.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/queryParam.mustache new file mode 100644 index 00000000000..c454950bd14 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/queryParam.mustache @@ -0,0 +1 @@ +{{#isQueryParam}}[FromQuery]{{#required}}[Required()]{{/required}}{{#pattern}}[RegularExpression("{{{pattern}}}")]{{/pattern}}{{#minLength}}{{#maxLength}}[StringLength({{maxLength}}, MinimumLength={{minLength}})]{{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} [MinLength({{minLength}})]{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} [MaxLength({{maxLength}})]{{/maxLength}}{{/minLength}}{{#minimum}}{{#maximum}}[Range({{minimum}}, {{maximum}})]{{/maximum}}{{/minimum}}{{&dataType}} {{paramName}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/tags.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/tags.mustache new file mode 100644 index 00000000000..c97df19949e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/tags.mustache @@ -0,0 +1 @@ +{{!TODO: Need iterable tags object...}}{{#tags}}, Tags = new[] { {{/tags}}"{{#tags}}{{tag}} {{/tags}}"{{#tags}} }{{/tags}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/typeConverter.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/typeConverter.mustache new file mode 100644 index 00000000000..dadf3a0260d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/typeConverter.mustache @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using Newtonsoft.Json; + +namespace {{packageName}}.Converters +{ + /// + /// Custom string to enum converter + /// + public class CustomEnumConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + var s = value as string; + if (string.IsNullOrEmpty(s)) + { + return null; + } + + return JsonConvert.DeserializeObject(@"""" + value.ToString() + @""""); + } + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharp-netcore-functions/CsharpNetcoreFunctionsServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharp-netcore-functions/CsharpNetcoreFunctionsServerCodegenTest.java index a62b73e7fae..0160f52d987 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharp-netcore-functions/CsharpNetcoreFunctionsServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/csharp-netcore-functions/CsharpNetcoreFunctionsServerCodegenTest.java @@ -27,11 +27,11 @@ public class CsharpNetcoreFunctionsServerCodegenTest { final CsharpNetcoreFunctionsServerCodegen codegen = new CsharpNetcoreFunctionsServerCodegen(); codegen.processOpts(); - Assert.assertEquals(codegen.toEnumVarName("FooBar", "string"), "FooBar"); - Assert.assertEquals(codegen.toEnumVarName("fooBar", "string"), "FooBar"); - Assert.assertEquals(codegen.toEnumVarName("foo-bar", "string"), "FooBar"); - Assert.assertEquals(codegen.toEnumVarName("foo_bar", "string"), "FooBar"); - Assert.assertEquals(codegen.toEnumVarName("foo bar", "string"), "FooBar"); + Assert.assertEquals(codegen.toEnumVarName("FooBar", "string"), "FooBarEnum"); + Assert.assertEquals(codegen.toEnumVarName("fooBar", "string"), "FooBarEnum"); + Assert.assertEquals(codegen.toEnumVarName("foo-bar", "string"), "FooBarEnum"); + Assert.assertEquals(codegen.toEnumVarName("foo_bar", "string"), "FooBarEnum"); + Assert.assertEquals(codegen.toEnumVarName("foo bar", "string"), "FooBarEnum"); // The below cases do not work currently, camelize doesn't support uppercase // Assert.assertEquals(codegen.toEnumVarName("FOO-BAR", "string"), "FooBar");