diff --git a/docs/generators.md b/docs/generators.md
index ed80e860e49..088acf1b859 100644
--- a/docs/generators.md
+++ b/docs/generators.md
@@ -85,6 +85,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](generators/csharp-netcore-functions.md)
* [csharp-nancyfx](generators/csharp-nancyfx.md)
* [erlang-server](generators/erlang-server.md)
* [fsharp-functions (beta)](generators/fsharp-functions.md)
diff --git a/docs/generators/csharp-netcore-functions.md b/docs/generators/csharp-netcore-functions.md
new file mode 100644
index 00000000000..50ec49b34a1
--- /dev/null
+++ b/docs/generators/csharp-netcore-functions.md
@@ -0,0 +1,300 @@
+---
+title: Config Options for csharp-netcore-functions
+sidebar_label: csharp-netcore-functions
+---
+
+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|
+|caseInsensitiveResponseHeaders|Make API response's headers case-insensitive| |false|
+|conditionalSerialization|Serialize only those properties which are initialized by user, accepted values are true or false, default value is false.| |false|
+|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false** The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications. **true** Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default. |true|
+|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
+|interfacePrefix|Prefix interfaces with a community standard or widely accepted prefix.| |I|
+|library|HTTP library template (sub-template) to use|**httpclient** HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.) **restsharp** RestSharp (https://github.com/restsharp/RestSharp) |restsharp|
+|licenseId|The identifier of the license| |null|
+|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |PascalCase|
+|netCoreProjectFile|Use the new format (.NET Core) for .NET project files (.csproj).| |false|
+|nonPublicApi|Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers.| |false|
+|nullableReferenceTypes|Use nullable annotations in the project. Only supported on C# 8 / ASP.NET Core 3.0 or newer.| |false|
+|optionalAssemblyInfo|Generate AssemblyInfo.cs.| |true|
+|optionalEmitDefaultValues|Set DataMember's EmitDefaultValue.| |false|
+|optionalMethodArgument|C# Optional method argument, e.g. void square(int x=10) (.net 4.0+ only).| |true|
+|optionalProjectFile|Generate {PackageName}.csproj.| |true|
+|packageGuid|The GUID that will be associated with the C# project| |null|
+|packageName|C# package name (convention: Title.Case).| |Org.OpenAPITools|
+|packageTags|Tags to identify the package| |null|
+|packageVersion|C# package version.| |1.0.0|
+|releaseNote|Release note, default to 'Minor update'.| |Minor update|
+|returnICollection|Return ICollection<T> instead of the concrete type.| |false|
+|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
+|sourceFolder|source folder for generated code| |src|
+|targetFramework|The target .NET framework version. To target multiple frameworks, use `;` as the separator, e.g. `netstandard2.1;netcoreapp3.0`|**netstandard1.3** .NET Standard 1.3 compatible **netstandard1.4** .NET Standard 1.4 compatible **netstandard1.5** .NET Standard 1.5 compatible **netstandard1.6** .NET Standard 1.6 compatible **netstandard2.0** .NET Standard 2.0 compatible **netstandard2.1** .NET Standard 2.1 compatible **netcoreapp2.0** .NET Core 2.0 compatible **netcoreapp2.1** .NET Core 2.1 compatible **netcoreapp3.0** .NET Core 3.0 compatible **netcoreapp3.1** .NET Core 3.1 compatible **net47** .NET Framework 4.7 compatible **net5.0** .NET 5.0 compatible |netstandard2.0|
+|useCollection|Deserialize array types to Collection<T> instead of List<T>.| |false|
+|useDateTimeOffset|Use DateTimeOffset to model date-time properties| |false|
+|useOneOfDiscriminatorLookup|Use the discriminator's mapping in oneOf to speed up the model lookup. IMPORTANT: Validation (e.g. one and only one match in oneOf's schemas) will be skipped.| |false|
+|validatable|Generates self-validatable models.| |true|
+
+## IMPORT MAPPING
+
+| Type/Alias | Imports |
+| ---------- | ------- |
+
+
+## INSTANTIATION TYPES
+
+| Type/Alias | Instantiated By |
+| ---------- | --------------- |
+|array|List|
+|list|List|
+|map|Dictionary|
+
+
+## LANGUAGE PRIMITIVES
+
+
+Boolean
+Collection
+DateTime
+DateTime?
+DateTimeOffset
+DateTimeOffset?
+Decimal
+Dictionary
+Double
+Float
+Guid
+Guid?
+ICollection
+Int32
+Int64
+List
+Object
+String
+System.IO.Stream
+bool
+bool?
+byte[]
+decimal
+decimal?
+double
+double?
+float
+float?
+int
+int?
+long
+long?
+string
+
+
+## RESERVED WORDS
+
+
+Client
+Configuration
+Version
+abstract
+as
+base
+bool
+break
+byte
+case
+catch
+char
+checked
+class
+client
+const
+continue
+decimal
+default
+delegate
+do
+double
+else
+enum
+event
+explicit
+extern
+false
+finally
+fixed
+float
+for
+foreach
+goto
+if
+implicit
+in
+int
+interface
+internal
+is
+localVarFileParams
+localVarFormParams
+localVarHeaderParams
+localVarHttpContentType
+localVarHttpContentTypes
+localVarHttpHeaderAccept
+localVarHttpHeaderAccepts
+localVarPath
+localVarPathParams
+localVarPostBody
+localVarQueryParams
+localVarResponse
+localVarStatusCode
+lock
+long
+namespace
+new
+null
+object
+operator
+out
+override
+parameter
+params
+private
+protected
+public
+readonly
+ref
+return
+sbyte
+sealed
+short
+sizeof
+stackalloc
+static
+string
+struct
+switch
+this
+throw
+true
+try
+typeof
+uint
+ulong
+unchecked
+unsafe
+ushort
+using
+virtual
+void
+volatile
+while
+
+
+## FEATURE SET
+
+
+### Client Modification Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasePath|✓|ToolingExtension
+|Authorizations|✗|ToolingExtension
+|UserAgent|✓|ToolingExtension
+|MockServer|✗|ToolingExtension
+
+### Data Type Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Custom|✗|OAS2,OAS3
+|Int32|✓|OAS2,OAS3
+|Int64|✓|OAS2,OAS3
+|Float|✓|OAS2,OAS3
+|Double|✓|OAS2,OAS3
+|Decimal|✓|ToolingExtension
+|String|✓|OAS2,OAS3
+|Byte|✓|OAS2,OAS3
+|Binary|✓|OAS2,OAS3
+|Boolean|✓|OAS2,OAS3
+|Date|✓|OAS2,OAS3
+|DateTime|✓|OAS2,OAS3
+|Password|✓|OAS2,OAS3
+|File|✓|OAS2
+|Array|✓|OAS2,OAS3
+|Maps|✓|ToolingExtension
+|CollectionFormat|✓|OAS2
+|CollectionFormatMulti|✓|OAS2
+|Enum|✓|OAS2,OAS3
+|ArrayOfEnum|✓|ToolingExtension
+|ArrayOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
+|ArrayOfCollectionOfModel|✓|ToolingExtension
+|ArrayOfCollectionOfEnum|✓|ToolingExtension
+|MapOfEnum|✓|ToolingExtension
+|MapOfModel|✓|ToolingExtension
+|MapOfCollectionOfPrimitives|✓|ToolingExtension
+|MapOfCollectionOfModel|✓|ToolingExtension
+|MapOfCollectionOfEnum|✓|ToolingExtension
+
+### Documentation Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Readme|✓|ToolingExtension
+|Model|✓|ToolingExtension
+|Api|✓|ToolingExtension
+
+### Global Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Host|✓|OAS2,OAS3
+|BasePath|✓|OAS2,OAS3
+|Info|✓|OAS2,OAS3
+|Schemes|✗|OAS2,OAS3
+|PartialSchemes|✓|OAS2,OAS3
+|Consumes|✓|OAS2
+|Produces|✓|OAS2
+|ExternalDocumentation|✓|OAS2,OAS3
+|Examples|✓|OAS2,OAS3
+|XMLStructureDefinitions|✗|OAS2,OAS3
+|MultiServer|✗|OAS3
+|ParameterizedServer|✗|OAS3
+|ParameterStyling|✗|OAS3
+|Callbacks|✗|OAS3
+|LinkObjects|✗|OAS3
+
+### Parameter Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Path|✓|OAS2,OAS3
+|Query|✓|OAS2,OAS3
+|Header|✓|OAS2,OAS3
+|Body|✓|OAS2
+|FormUnencoded|✓|OAS2
+|FormMultipart|✓|OAS2
+|Cookie|✓|OAS3
+
+### Schema Support Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|Simple|✓|OAS2,OAS3
+|Composite|✓|OAS2,OAS3
+|Polymorphism|✓|OAS2,OAS3
+|Union|✗|OAS3
+
+### Security Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasicAuth|✓|OAS2,OAS3
+|ApiKey|✓|OAS2,OAS3
+|OpenIDConnect|✗|OAS3
+|BearerToken|✗|OAS3
+|OAuth2_Implicit|✓|OAS2,OAS3
+|OAuth2_Password|✗|OAS2,OAS3
+|OAuth2_ClientCredentials|✗|OAS2,OAS3
+|OAuth2_AuthorizationCode|✗|OAS2,OAS3
+
+### Wire Format Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|JSON|✓|OAS2,OAS3
+|XML|✓|OAS2,OAS3
+|PROTOBUF|✗|ToolingExtension
+|Custom|✗|OAS2,OAS3
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreReducedClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreReducedClientCodegen.java
new file mode 100644
index 00000000000..8f189b444ba
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreReducedClientCodegen.java
@@ -0,0 +1,1151 @@
+/*
+ * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openapitools.codegen.languages;
+
+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.ComposedSchema;
+import io.swagger.v3.oas.models.media.Schema;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.meta.features.*;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.openapitools.codegen.utils.ProcessUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+import static org.openapitools.codegen.utils.StringUtils.camelize;
+import static org.openapitools.codegen.utils.StringUtils.underscore;
+
+@SuppressWarnings("Duplicates")
+public class CSharpNetCoreReducedClientCodegen extends AbstractCSharpCodegen {
+ // Defines the sdk option for targeted frameworks, which differs from targetFramework and targetFrameworkNuget
+ protected static final String MCS_NET_VERSION_KEY = "x-mcs-sdk";
+ protected static final String SUPPORTS_UWP = "supportsUWP";
+ protected static final String SUPPORTS_RETRY = "supportsRetry";
+
+ protected static final String NET_STANDARD = "netStandard";
+
+ // HTTP libraries
+ protected static final String RESTSHARP = "restsharp";
+ protected static final String HTTPCLIENT = "httpclient";
+
+ // Project Variable, determined from target framework. Not intended to be user-settable.
+ protected static final String TARGET_FRAMEWORK_IDENTIFIER = "targetFrameworkIdentifier";
+ // Project Variable, determined from target framework. Not intended to be user-settable.
+ protected static final String TARGET_FRAMEWORK_VERSION = "targetFrameworkVersion";
+
+ @SuppressWarnings("hiding")
+ private final Logger LOGGER = LoggerFactory.getLogger(CSharpClientCodegen.class);
+ private static final List frameworkStrategies = Arrays.asList(
+ FrameworkStrategy.NETSTANDARD_1_3,
+ FrameworkStrategy.NETSTANDARD_1_4,
+ FrameworkStrategy.NETSTANDARD_1_5,
+ FrameworkStrategy.NETSTANDARD_1_6,
+ FrameworkStrategy.NETSTANDARD_2_0,
+ FrameworkStrategy.NETSTANDARD_2_1,
+ FrameworkStrategy.NETCOREAPP_2_0,
+ FrameworkStrategy.NETCOREAPP_2_1,
+ FrameworkStrategy.NETCOREAPP_3_0,
+ FrameworkStrategy.NETCOREAPP_3_1,
+ FrameworkStrategy.NETFRAMEWORK_4_7,
+ FrameworkStrategy.NET_5_0
+ );
+ private static FrameworkStrategy defaultFramework = FrameworkStrategy.NETSTANDARD_2_0;
+ protected final Map frameworks;
+ protected String packageGuid = "{" + java.util.UUID.randomUUID().toString().toUpperCase(Locale.ROOT) + "}";
+ protected String clientPackage = "Org.OpenAPITools.Client";
+ protected String apiDocPath = "docs/";
+ protected String modelDocPath = "docs/";
+
+ // Defines TargetFrameworkVersion in csproj files
+ protected String targetFramework = defaultFramework.name;
+ protected String testTargetFramework = defaultFramework.testTargetFramework;
+
+ // Defines nuget identifiers for target framework
+ protected String targetFrameworkNuget = targetFramework;
+
+ protected boolean supportsRetry = Boolean.TRUE;
+ protected boolean supportsAsync = Boolean.TRUE;
+ protected boolean netStandard = Boolean.FALSE;
+
+ protected boolean validatable = Boolean.TRUE;
+ protected Map regexModifiers;
+ // By default, generated code is considered public
+ protected boolean nonPublicApi = Boolean.FALSE;
+
+ protected boolean caseInsensitiveResponseHeaders = Boolean.FALSE;
+ protected String releaseNote = "Minor update";
+ protected String licenseId;
+ protected String packageTags;
+ protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup
+
+ protected boolean needsCustomHttpMethod = false;
+ protected boolean needsUriBuilder = false;
+
+ public CSharpNetCoreReducedClientCodegen() {
+ super();
+
+ modifyFeatureSet(features -> features
+ .includeDocumentationFeatures(DocumentationFeature.Readme)
+ .securityFeatures(EnumSet.of(
+ SecurityFeature.OAuth2_Implicit,
+ SecurityFeature.BasicAuth,
+ SecurityFeature.ApiKey
+ ))
+ .excludeGlobalFeatures(
+ GlobalFeature.XMLStructureDefinitions,
+ GlobalFeature.Callbacks,
+ GlobalFeature.LinkObjects,
+ GlobalFeature.ParameterStyling
+ )
+ .includeSchemaSupportFeatures(
+ SchemaSupportFeature.Polymorphism
+ )
+ .includeParameterFeatures(
+ ParameterFeature.Cookie
+ )
+ .includeClientModificationFeatures(
+ ClientModificationFeature.BasePath,
+ ClientModificationFeature.UserAgent
+ )
+ );
+
+ // mapped non-nullable type without ?
+ typeMapping = new HashMap();
+ 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("decimal", "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");
+ typeMapping.put("URI", "string");
+ typeMapping.put("AnyType", "Object");
+
+ 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-netcore";
+
+ cliOptions.clear();
+
+ // CLI options
+ addOption(CodegenConstants.PACKAGE_NAME,
+ "C# package name (convention: Title.Case).",
+ this.packageName);
+
+ addOption(CodegenConstants.PACKAGE_VERSION,
+ "C# package version.",
+ this.packageVersion);
+
+ addOption(CodegenConstants.SOURCE_FOLDER,
+ CodegenConstants.SOURCE_FOLDER_DESC,
+ sourceFolder);
+
+ addOption(CodegenConstants.OPTIONAL_PROJECT_GUID,
+ CodegenConstants.OPTIONAL_PROJECT_GUID_DESC,
+ null);
+
+ addOption(CodegenConstants.INTERFACE_PREFIX,
+ CodegenConstants.INTERFACE_PREFIX_DESC,
+ interfacePrefix);
+
+ addOption(CodegenConstants.LICENSE_ID,
+ CodegenConstants.LICENSE_ID_DESC,
+ this.licenseId);
+
+ addOption(CodegenConstants.RELEASE_NOTE,
+ CodegenConstants.RELEASE_NOTE_DESC,
+ this.releaseNote);
+
+ addOption(CodegenConstants.PACKAGE_TAGS,
+ CodegenConstants.PACKAGE_TAGS_DESC,
+ this.packageTags);
+
+ CliOption framework = new CliOption(
+ CodegenConstants.DOTNET_FRAMEWORK,
+ CodegenConstants.DOTNET_FRAMEWORK_DESC
+ );
+
+ CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
+ CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
+ CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString());
+ Map disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
+ disallowAdditionalPropertiesIfNotPresentOpts.put("false",
+ "The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
+ disallowAdditionalPropertiesIfNotPresentOpts.put("true",
+ "Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.");
+ disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
+ cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
+ this.setDisallowAdditionalPropertiesIfNotPresent(true);
+
+ ImmutableMap.Builder frameworkBuilder = new ImmutableMap.Builder<>();
+ for (FrameworkStrategy frameworkStrategy : frameworkStrategies) {
+ frameworkBuilder.put(frameworkStrategy.name, frameworkStrategy.description);
+ }
+
+ frameworks = frameworkBuilder.build();
+
+ framework.defaultValue(this.targetFramework);
+ framework.setEnum(frameworks);
+ cliOptions.add(framework);
+
+ CliOption modelPropertyNaming = new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC);
+ cliOptions.add(modelPropertyNaming.defaultValue("PascalCase"));
+
+ // CLI Switches
+ addSwitch(CodegenConstants.NULLABLE_REFERENCE_TYPES,
+ CodegenConstants.NULLABLE_REFERENCE_TYPES_DESC,
+ this.nullReferenceTypesFlag);
+
+ addSwitch(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
+ CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC,
+ this.hideGenerationTimestamp);
+
+ addSwitch(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG,
+ CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC,
+ this.sortParamsByRequiredFlag);
+
+ addSwitch(CodegenConstants.USE_DATETIME_OFFSET,
+ CodegenConstants.USE_DATETIME_OFFSET_DESC,
+ this.useDateTimeOffsetFlag);
+
+ addSwitch(CodegenConstants.USE_COLLECTION,
+ CodegenConstants.USE_COLLECTION_DESC,
+ this.useCollection);
+
+ addSwitch(CodegenConstants.RETURN_ICOLLECTION,
+ CodegenConstants.RETURN_ICOLLECTION_DESC,
+ this.returnICollection);
+
+ addSwitch(CodegenConstants.OPTIONAL_METHOD_ARGUMENT,
+ "C# Optional method argument, e.g. void square(int x=10) (.net 4.0+ only).",
+ this.optionalMethodArgumentFlag);
+
+ addSwitch(CodegenConstants.OPTIONAL_ASSEMBLY_INFO,
+ CodegenConstants.OPTIONAL_ASSEMBLY_INFO_DESC,
+ this.optionalAssemblyInfoFlag);
+
+ addSwitch(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES,
+ CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES_DESC,
+ this.optionalEmitDefaultValuesFlag);
+
+ addSwitch(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION,
+ CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION_DESC,
+ this.conditionalSerialization);
+
+ addSwitch(CodegenConstants.OPTIONAL_PROJECT_FILE,
+ CodegenConstants.OPTIONAL_PROJECT_FILE_DESC,
+ this.optionalProjectFileFlag);
+
+ // NOTE: This will reduce visibility of all public members in templates. Users can use InternalsVisibleTo
+ // https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute(v=vs.110).aspx
+ // to expose to shared code if the generated code is not embedded into another project. Otherwise, users of codegen
+ // should rely on default public visibility.
+ addSwitch(CodegenConstants.NON_PUBLIC_API,
+ CodegenConstants.NON_PUBLIC_API_DESC,
+ this.nonPublicApi);
+
+ addSwitch(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS,
+ CodegenConstants.ALLOW_UNICODE_IDENTIFIERS_DESC,
+ this.allowUnicodeIdentifiers);
+
+ addSwitch(CodegenConstants.NETCORE_PROJECT_FILE,
+ CodegenConstants.NETCORE_PROJECT_FILE_DESC,
+ this.netCoreProjectFileFlag);
+
+ addSwitch(CodegenConstants.VALIDATABLE,
+ CodegenConstants.VALIDATABLE_DESC,
+ this.validatable);
+
+ addSwitch(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP,
+ CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC,
+ this.caseInsensitiveResponseHeaders);
+
+ addSwitch(CodegenConstants.CASE_INSENSITIVE_RESPONSE_HEADERS,
+ CodegenConstants.CASE_INSENSITIVE_RESPONSE_HEADERS_DESC,
+ this.caseInsensitiveResponseHeaders);
+
+ regexModifiers = new HashMap<>();
+ regexModifiers.put('i', "IgnoreCase");
+ regexModifiers.put('m', "Multiline");
+ regexModifiers.put('s', "Singleline");
+ regexModifiers.put('x', "IgnorePatternWhitespace");
+
+ supportedLibraries.put(HTTPCLIENT, "HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) "
+ + "(Experimental. May subject to breaking changes without further notice.)");
+ supportedLibraries.put(RESTSHARP, "RestSharp (https://github.com/restsharp/RestSharp)");
+
+ CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "HTTP library template (sub-template) to use");
+ libraryOption.setEnum(supportedLibraries);
+ // set RESTSHARP as the default
+ libraryOption.setDefault(RESTSHARP);
+ cliOptions.add(libraryOption);
+ setLibrary(RESTSHARP);
+ }
+
+ @Override
+ public String apiDocFileFolder() {
+ return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
+ }
+
+ @Override
+ public String apiTestFileFolder() {
+ return outputFolder + File.separator + testFolder + File.separator + testPackageName() + File.separator + apiPackage();
+ }
+
+ @Override
+ public CodegenModel fromModel(String name, Schema model) {
+ Map allDefinitions = ModelUtils.getSchemas(this.openAPI);
+ CodegenModel codegenModel = super.fromModel(name, model);
+ if (allDefinitions != null && codegenModel != null && codegenModel.parent != null) {
+ final Schema parentModel = allDefinitions.get(toModelName(codegenModel.parent));
+ if (parentModel != null) {
+ final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel);
+ if (codegenModel.hasEnums) {
+ codegenModel = this.reconcileInlineEnums(codegenModel, parentCodegenModel);
+ }
+
+ Map propertyHash = new HashMap<>(codegenModel.vars.size());
+ for (final CodegenProperty property : codegenModel.vars) {
+ propertyHash.put(property.name, property);
+ }
+
+ for (final CodegenProperty property : codegenModel.readWriteVars) {
+ if (property.defaultValue == null && parentCodegenModel.discriminator != null && property.name.equals(parentCodegenModel.discriminator.getPropertyName())) {
+ property.defaultValue = "\"" + name + "\"";
+ }
+ }
+
+ CodegenProperty last = null;
+ for (final CodegenProperty property : parentCodegenModel.vars) {
+ // helper list of parentVars simplifies templating
+ if (!propertyHash.containsKey(property.name)) {
+ final CodegenProperty parentVar = property.clone();
+ parentVar.isInherited = true;
+ last = parentVar;
+ LOGGER.debug("adding parent variable {}", property.name);
+ codegenModel.parentVars.add(parentVar);
+ }
+ }
+ }
+ }
+
+ // Cleanup possible duplicates. Currently, readWriteVars can contain the same property twice. May or may not be isolated to C#.
+ if (codegenModel != null && codegenModel.readWriteVars != null && codegenModel.readWriteVars.size() > 1) {
+ int length = codegenModel.readWriteVars.size() - 1;
+ for (int i = length; i > (length / 2); i--) {
+ final CodegenProperty codegenProperty = codegenModel.readWriteVars.get(i);
+ // If the property at current index is found earlier in the list, remove this last instance.
+ if (codegenModel.readWriteVars.indexOf(codegenProperty) < i) {
+ codegenModel.readWriteVars.remove(i);
+ }
+ }
+ }
+
+ return codegenModel;
+ }
+
+ @Override
+ public String getHelp() {
+ return "Generates a C# client library (.NET Standard, .NET Core).";
+ }
+
+ public String getModelPropertyNaming() {
+ return this.modelPropertyNaming;
+ }
+
+ public void setModelPropertyNaming(String naming) {
+ if ("original".equals(naming) || "camelCase".equals(naming) ||
+ "PascalCase".equals(naming) || "snake_case".equals(naming)) {
+ this.modelPropertyNaming = naming;
+ } else {
+ throw new IllegalArgumentException("Invalid model property naming '" +
+ naming + "'. Must be 'original', 'camelCase', " +
+ "'PascalCase' or 'snake_case'");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "csharp-netcore";
+ }
+
+ public String getNameUsingModelPropertyNaming(String name) {
+ switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) {
+ case original:
+ return name;
+ case camelCase:
+ return camelize(name, true);
+ case PascalCase:
+ return camelize(name);
+ case snake_case:
+ return underscore(name);
+ default:
+ throw new IllegalArgumentException("Invalid model property naming '" +
+ name + "'. Must be 'original', 'camelCase', " +
+ "'PascalCase' or 'snake_case'");
+ }
+ }
+
+ @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;
+ }
+ }
+
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.CLIENT;
+ }
+
+ public boolean isNonPublicApi() {
+ return nonPublicApi;
+ }
+
+ public void setNonPublicApi(final boolean nonPublicApi) {
+ this.nonPublicApi = nonPublicApi;
+ }
+
+ @Override
+ public String modelDocFileFolder() {
+ return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
+ }
+
+ @Override
+ public String modelTestFileFolder() {
+ return outputFolder + File.separator + testFolder + File.separator + testPackageName() + File.separator + modelPackage();
+ }
+
+ @Override
+ public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
+ postProcessPattern(property.pattern, property.vendorExtensions);
+ postProcessEmitDefaultValue(property.vendorExtensions);
+
+ super.postProcessModelProperty(model, property);
+ }
+
+ @Override
+ public void postProcessParameter(CodegenParameter parameter) {
+ postProcessPattern(parameter.pattern, parameter.vendorExtensions);
+ postProcessEmitDefaultValue(parameter.vendorExtensions);
+ super.postProcessParameter(parameter);
+ }
+
+ /*
+ * The 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
+ * format and apply modifiers in a compatible way.
+ * See https://msdn.microsoft.com/en-us/library/yd1hzczs(v=vs.110).aspx for .NET options.
+ */
+ public void postProcessPattern(String pattern, Map vendorExtensions) {
+ if (pattern != null) {
+ int i = pattern.lastIndexOf('/');
+
+ //Must follow Perl /pattern/modifiers convention
+ if (pattern.charAt(0) != '/' || i < 2) {
+ throw new IllegalArgumentException("Pattern must follow the Perl "
+ + "/pattern/modifiers convention. " + pattern + " is not valid.");
+ }
+
+ String regex = pattern.substring(1, i).replace("'", "\'").replace("\"", "\"\"");
+ List modifiers = new ArrayList();
+
+ // perl requires an explicit modifier to be culture specific and .NET is the reverse.
+ modifiers.add("CultureInvariant");
+
+ for (char c : pattern.substring(i).toCharArray()) {
+ if (regexModifiers.containsKey(c)) {
+ String modifier = regexModifiers.get(c);
+ modifiers.add(modifier);
+ } else if (c == 'l') {
+ modifiers.remove("CultureInvariant");
+ }
+ }
+
+ vendorExtensions.put("x-regex", regex);
+ vendorExtensions.put("x-modifiers", modifiers);
+ }
+ }
+
+ public void postProcessEmitDefaultValue(Map vendorExtensions) {
+ vendorExtensions.put("x-emit-default-value", optionalEmitDefaultValuesFlag);
+ }
+
+ @Override
+ public Mustache.Compiler processCompiler(Mustache.Compiler compiler) {
+ // To avoid unexpected behaviors when options are passed programmatically such as { "supportsAsync": "" }
+ return super.processCompiler(compiler).emptyStringIsFalse(true);
+ }
+
+ @Override
+ public void processOpts() {
+ this.setLegacyDiscriminatorBehavior(false);
+
+ super.processOpts();
+
+ /*
+ * NOTE: When supporting boolean additionalProperties, you should read the value and write it back as a boolean.
+ * This avoids oddities where additionalProperties contains "false" rather than false, which will cause the
+ * templating engine to behave unexpectedly.
+ *
+ * Use the pattern:
+ * if (additionalProperties.containsKey(prop)) convertPropertyToBooleanAndWriteBack(prop);
+ */
+
+ if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) {
+ this.setDisallowAdditionalPropertiesIfNotPresent(Boolean.parseBoolean(additionalProperties
+ .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString()));
+ }
+
+ if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES)) {
+ setOptionalEmitDefaultValuesFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES));
+ } else {
+ additionalProperties.put(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES, optionalEmitDefaultValuesFlag);
+ }
+
+ if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION)) {
+ setConditionalSerialization(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION));
+ } else {
+ additionalProperties.put(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION, conditionalSerialization);
+ }
+
+ if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) {
+ setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
+ }
+
+ if (isEmpty(apiPackage)) {
+ setApiPackage("Api");
+ }
+ if (isEmpty(modelPackage)) {
+ setModelPackage("Model");
+ }
+
+ clientPackage = "Client";
+
+ if (RESTSHARP.equals(getLibrary())) {
+ additionalProperties.put("useRestSharp", true);
+ needsCustomHttpMethod = true;
+ } else if (HTTPCLIENT.equals(getLibrary())) {
+ setLibrary(HTTPCLIENT);
+ additionalProperties.put("useHttpClient", true);
+ needsUriBuilder = true;
+ } else {
+ throw new RuntimeException("Invalid HTTP library " + getLibrary() + ". Only restsharp, httpclient are supported.");
+ }
+
+ String inputFramework = (String) additionalProperties.getOrDefault(CodegenConstants.DOTNET_FRAMEWORK, defaultFramework.name);
+ String[] frameworks;
+ List strategies = new ArrayList<>();
+
+ if (inputFramework.contains(";")) {
+ // multiple target framework
+ frameworks = inputFramework.split(";");
+ additionalProperties.put("multiTarget", true);
+ } else {
+ // just a single value
+ frameworks = new String [] {inputFramework};
+ }
+
+ for (String framework : frameworks) {
+ boolean strategyMatched = false;
+ for (FrameworkStrategy frameworkStrategy : frameworkStrategies) {
+ if (framework.equals(frameworkStrategy.name)) {
+ strategies.add(frameworkStrategy);
+ strategyMatched = true;
+ }
+
+ if (frameworkStrategy != FrameworkStrategy.NETSTANDARD_2_0 && "restsharp".equals(getLibrary())) {
+ LOGGER.warn("If using built-in templates, RestSharp only supports netstandard 2.0 or later.");
+ }
+ }
+
+ if (!strategyMatched) {
+ // throws exception if the input targetFramework is invalid
+ throw new IllegalArgumentException("The input (" + inputFramework + ") contains Invalid .NET framework version: " +
+ framework + ". List of supported versions: " +
+ frameworkStrategies.stream()
+ .map(p -> p.name)
+ .collect(Collectors.joining(", ")));
+ }
+ }
+
+ configureAdditionalPropertiesForFrameworks(additionalProperties, strategies);
+ setTargetFrameworkNuget(strategies);
+ setTargetFramework(strategies);
+ setTestTargetFramework(strategies);
+
+ setSupportsAsync(Boolean.TRUE);
+ setNetStandard(strategies.stream().anyMatch(p -> Boolean.TRUE.equals(p.isNetStandard)));
+
+ if (!netStandard) {
+ setNetCoreProjectFileFlag(true);
+ }
+
+ if (additionalProperties.containsKey(CodegenConstants.GENERATE_PROPERTY_CHANGED)) {
+ LOGGER.warn("{} is not supported in the .NET Standard generator.", CodegenConstants.GENERATE_PROPERTY_CHANGED);
+ additionalProperties.remove(CodegenConstants.GENERATE_PROPERTY_CHANGED);
+ }
+
+ final AtomicReference excludeTests = new AtomicReference<>();
+ syncBooleanProperty(additionalProperties, CodegenConstants.EXCLUDE_TESTS, excludeTests::set, false);
+
+ syncStringProperty(additionalProperties, "clientPackage", (s) -> { }, clientPackage);
+
+ syncStringProperty(additionalProperties, CodegenConstants.API_PACKAGE, this::setApiPackage, apiPackage);
+ syncStringProperty(additionalProperties, CodegenConstants.MODEL_PACKAGE, this::setModelPackage, modelPackage);
+ syncStringProperty(additionalProperties, CodegenConstants.OPTIONAL_PROJECT_GUID, this::setPackageGuid, packageGuid);
+ syncStringProperty(additionalProperties, "targetFrameworkNuget", this::setTargetFrameworkNuget, this.targetFrameworkNuget);
+ syncStringProperty(additionalProperties, "testTargetFramework", this::setTestTargetFramework, this.testTargetFramework);
+
+ syncBooleanProperty(additionalProperties, "netStandard", this::setNetStandard, this.netStandard);
+
+ syncBooleanProperty(additionalProperties, CodegenConstants.VALIDATABLE, this::setValidatable, this.validatable);
+ syncBooleanProperty(additionalProperties, CodegenConstants.SUPPORTS_ASYNC, this::setSupportsAsync, this.supportsAsync);
+ syncBooleanProperty(additionalProperties, SUPPORTS_RETRY, this::setSupportsRetry, this.supportsRetry);
+ syncBooleanProperty(additionalProperties, CodegenConstants.OPTIONAL_METHOD_ARGUMENT, this::setOptionalMethodArgumentFlag, optionalMethodArgumentFlag);
+ syncBooleanProperty(additionalProperties, CodegenConstants.NON_PUBLIC_API, this::setNonPublicApi, isNonPublicApi());
+ syncBooleanProperty(additionalProperties, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, this::setUseOneOfDiscriminatorLookup, this.useOneOfDiscriminatorLookup);
+
+ final String testPackageName = testPackageName();
+ String packageFolder = sourceFolder + File.separator + packageName;
+ String clientPackageDir = packageFolder + File.separator + clientPackage;
+ String modelPackageDir = packageFolder + File.separator + modelPackage;
+ String testPackageFolder = testFolder + File.separator + testPackageName;
+
+ additionalProperties.put("testPackageName", testPackageName);
+
+ //Compute the relative path to the bin directory where the external assemblies live
+ //This is necessary to properly generate the project file
+ int packageDepth = packageFolder.length() - packageFolder.replace(java.io.File.separator, "").length();
+ String binRelativePath = "..\\";
+ for (int i = 0; i < packageDepth; i = i + 1) {
+ binRelativePath += "..\\";
+ }
+ binRelativePath += "vendor";
+ additionalProperties.put("binRelativePath", binRelativePath);
+
+ if(HTTPCLIENT.equals(getLibrary())) {
+ supportingFiles.add(new SupportingFile("FileParameter.mustache", clientPackageDir, "FileParameter.cs"));
+ typeMapping.put("file", "FileParameter");
+ }
+
+ 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"));
+ supportingFiles.add(new SupportingFile("ClientUtils.mustache", clientPackageDir, "ClientUtils.cs"));
+ if(needsCustomHttpMethod) {
+ supportingFiles.add(new SupportingFile("HttpMethod.mustache", clientPackageDir, "HttpMethod.cs"));
+ }
+ if(needsUriBuilder) {
+ supportingFiles.add(new SupportingFile("WebRequestPathBuilder.mustache", clientPackageDir, "WebRequestPathBuilder.cs"));
+ }
+ if (ProcessUtils.hasHttpSignatureMethods(openAPI)) {
+ supportingFiles.add(new SupportingFile("HttpSigningConfiguration.mustache", clientPackageDir, "HttpSigningConfiguration.cs"));
+ }
+ if (supportsAsync) {
+ supportingFiles.add(new SupportingFile("IAsynchronousClient.mustache", clientPackageDir, "IAsynchronousClient.cs"));
+ }
+ supportingFiles.add(new SupportingFile("ISynchronousClient.mustache", clientPackageDir, "ISynchronousClient.cs"));
+ supportingFiles.add(new SupportingFile("RequestOptions.mustache", clientPackageDir, "RequestOptions.cs"));
+ supportingFiles.add(new SupportingFile("Multimap.mustache", clientPackageDir, "Multimap.cs"));
+
+ if (supportsRetry) {
+ supportingFiles.add(new SupportingFile("RetryConfiguration.mustache", clientPackageDir, "RetryConfiguration.cs"));
+ }
+
+ supportingFiles.add(new SupportingFile("IReadableConfiguration.mustache",
+ clientPackageDir, "IReadableConfiguration.cs"));
+ supportingFiles.add(new SupportingFile("GlobalConfiguration.mustache",
+ clientPackageDir, "GlobalConfiguration.cs"));
+
+ // Only write out test related files if excludeTests is unset or explicitly set to false (see start of this method)
+ if (Boolean.FALSE.equals(excludeTests.get())) {
+ modelTestTemplateFiles.put("model_test.mustache", ".cs");
+ apiTestTemplateFiles.put("api_test.mustache", ".cs");
+ }
+
+ supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
+ supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
+ supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
+
+ supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml"));
+ supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs"));
+
+ additionalProperties.put("apiDocPath", apiDocPath);
+ additionalProperties.put("modelDocPath", modelDocPath);
+ }
+
+ public void setNetStandard(Boolean netStandard) {
+ this.netStandard = netStandard;
+ }
+
+ public void setOptionalAssemblyInfoFlag(boolean flag) {
+ this.optionalAssemblyInfoFlag = flag;
+ }
+
+ public void setOptionalEmitDefaultValuesFlag(boolean flag) {
+ this.optionalEmitDefaultValuesFlag = flag;
+ }
+
+ public void setConditionalSerialization(boolean flag){
+ this.conditionalSerialization = flag;
+ }
+
+ public void setOptionalProjectFileFlag(boolean flag) {
+ this.optionalProjectFileFlag = flag;
+ }
+
+ public void setPackageGuid(String packageGuid) {
+ this.packageGuid = packageGuid;
+ }
+
+ @Override
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+ }
+
+ @Override
+ public void setPackageVersion(String packageVersion) {
+ this.packageVersion = packageVersion;
+ }
+
+ public void setSupportsAsync(Boolean supportsAsync) {
+ this.supportsAsync = supportsAsync;
+ }
+
+ public void setSupportsRetry(Boolean supportsRetry) {
+ this.supportsRetry = supportsRetry;
+ }
+
+ public void setTargetFramework(String dotnetFramework) {
+ if (!frameworks.containsKey(dotnetFramework)) {
+ throw new IllegalArgumentException("Invalid .NET framework version: " +
+ dotnetFramework + ". List of supported versions: " +
+ frameworkStrategies.stream()
+ .map(p -> p.name)
+ .collect(Collectors.joining(", ")));
+ } else {
+ this.targetFramework = dotnetFramework;
+ }
+ LOGGER.info("Generating code for .NET Framework {}", this.targetFramework);
+ }
+
+ public void setTargetFramework(List strategies) {
+ for (FrameworkStrategy strategy : strategies) {
+ if (!frameworks.containsKey(strategy.name)) {
+ throw new IllegalArgumentException("Invalid .NET framework version: " +
+ strategy.name + ". List of supported versions: " +
+ frameworkStrategies.stream()
+ .map(p -> p.name)
+ .collect(Collectors.joining(", ")));
+ }
+ }
+ this.targetFramework = strategies.stream().map(p -> p.name)
+ .collect(Collectors.joining(";"));
+ LOGGER.info("Generating code for .NET Framework {}", this.targetFramework);
+ }
+
+ public void setTestTargetFramework(String testTargetFramework) {
+ this.testTargetFramework = testTargetFramework;
+ }
+
+ public void setTestTargetFramework(List strategies) {
+ this.testTargetFramework = strategies.stream().map(p -> p.testTargetFramework)
+ .collect(Collectors.joining(";"));
+ }
+
+ public void setTargetFrameworkNuget(String targetFrameworkNuget) {
+ this.targetFrameworkNuget = targetFrameworkNuget;
+ }
+
+ public void setTargetFrameworkNuget(List strategies) {
+ this.targetFrameworkNuget = strategies.stream().map(p -> p.getNugetFrameworkIdentifier())
+ .collect(Collectors.joining(";"));
+ }
+
+ public void setValidatable(boolean validatable) {
+ this.validatable = validatable;
+ }
+
+ public void setCaseInsensitiveResponseHeaders(final Boolean caseInsensitiveResponseHeaders) {
+ this.caseInsensitiveResponseHeaders = caseInsensitiveResponseHeaders;
+ }
+
+ public void setLicenseId(String licenseId) {
+ this.licenseId = licenseId;
+ }
+
+ @Override
+ public void setReleaseNote(String releaseNote) {
+ this.releaseNote = releaseNote;
+ }
+
+ public void setPackageTags(String packageTags) {
+ this.packageTags = packageTags;
+ }
+
+ public void setUseOneOfDiscriminatorLookup(boolean useOneOfDiscriminatorLookup) {
+ this.useOneOfDiscriminatorLookup = useOneOfDiscriminatorLookup;
+ }
+
+ public boolean getUseOneOfDiscriminatorLookup() {
+ return this.useOneOfDiscriminatorLookup;
+ }
+
+ @Override
+ public String toEnumVarName(String value, String datatype) {
+ if (value.length() == 0) {
+ return "Empty";
+ }
+
+ // for symbol, e.g. $, #
+ if (getSymbolName(value) != null) {
+ return camelize(getSymbolName(value));
+ }
+
+ // number
+ if (datatype.startsWith("int") || datatype.startsWith("long") ||
+ datatype.startsWith("double") || datatype.startsWith("float")) {
+ String varName = "NUMBER_" + value;
+ varName = varName.replaceAll("-", "MINUS_");
+ varName = varName.replaceAll("\\+", "PLUS_");
+ varName = varName.replaceAll("\\.", "_DOT_");
+ return varName;
+ }
+
+ // string
+ String var = value.replaceAll(" ", "_");
+ var = camelize(var);
+ var = var.replaceAll("\\W+", "");
+
+ if (var.matches("\\d.*")) {
+ return "_" + var;
+ } else {
+ return var;
+ }
+ }
+
+ @Override
+ public String toModelDocFilename(String name) {
+ return toModelFilename(name);
+ }
+
+ @Override
+ public String toVarName(String name) {
+ // sanitize name
+ name = sanitizeName(name);
+
+ // if it's all upper case, do nothing
+ if (name.matches("^[A-Z_]*$")) {
+ return name;
+ }
+
+ name = getNameUsingModelPropertyNaming(name);
+
+ // for reserved word or word starting with number, append _
+ if (isReservedWord(name) || name.matches("^\\d.*")) {
+ name = escapeReservedWord(name);
+ }
+
+ // for function names in the model, escape with the "Property" prefix
+ if (propertySpecialKeywords.contains(name)) {
+ return camelize("property_" + name);
+ }
+
+ return name;
+ }
+
+ private CodegenModel reconcileInlineEnums(CodegenModel codegenModel, CodegenModel parentCodegenModel) {
+ // This generator uses inline classes to define enums, which breaks when
+ // dealing with models that have subTypes. To clean this up, we will analyze
+ // the parent and child models, look for enums that match, and remove
+ // them from the child models and leave them in the parent.
+ // Because the child models extend the parents, the enums will be available via the parent.
+
+ // Only bother with reconciliation if the parent model has enums.
+ if (parentCodegenModel.hasEnums) {
+
+ // Get the properties for the parent and child models
+ final List parentModelCodegenProperties = parentCodegenModel.vars;
+ List codegenProperties = codegenModel.vars;
+
+ // Iterate over all of the parent model properties
+ boolean removedChildEnum = false;
+ for (CodegenProperty parentModelCodegenProperty : parentModelCodegenProperties) {
+ // Look for enums
+ if (parentModelCodegenProperty.isEnum) {
+ // Now that we have found an enum in the parent class,
+ // and search the child class for the same enum.
+ Iterator iterator = codegenProperties.iterator();
+ while (iterator.hasNext()) {
+ CodegenProperty codegenProperty = iterator.next();
+ if (codegenProperty.isEnum && codegenProperty.equals(parentModelCodegenProperty)) {
+ // We found an enum in the child class that is
+ // a duplicate of the one in the parent, so remove it.
+ iterator.remove();
+ removedChildEnum = true;
+ }
+ }
+ }
+ }
+
+ if (removedChildEnum) {
+ codegenModel.vars = codegenProperties;
+ }
+ }
+
+ return codegenModel;
+ }
+
+ private void syncBooleanProperty(final Map additionalProperties, final String key, final Consumer setter, final Boolean defaultValue) {
+ if (additionalProperties.containsKey(key)) {
+ setter.accept(convertPropertyToBooleanAndWriteBack(key));
+ } else {
+ additionalProperties.put(key, defaultValue);
+ setter.accept(defaultValue);
+ }
+ }
+
+ private void syncStringProperty(final Map additionalProperties, final String key, final Consumer setter, final String defaultValue) {
+ if (additionalProperties.containsKey(key)) {
+ setter.accept((String) additionalProperties.get(key));
+ } else {
+ additionalProperties.put(key, defaultValue);
+ setter.accept(defaultValue);
+ }
+ }
+
+ // https://docs.microsoft.com/en-us/dotnet/standard/net-standard
+ @SuppressWarnings("Duplicates")
+ private static abstract class FrameworkStrategy {
+
+ private final Logger LOGGER = LoggerFactory.getLogger(CSharpClientCodegen.class);
+
+ static FrameworkStrategy NETSTANDARD_1_3 = new FrameworkStrategy("netstandard1.3", ".NET Standard 1.3 compatible", "netcoreapp2.0") {
+ };
+ static FrameworkStrategy NETSTANDARD_1_4 = new FrameworkStrategy("netstandard1.4", ".NET Standard 1.4 compatible", "netcoreapp2.0") {
+ };
+ static FrameworkStrategy NETSTANDARD_1_5 = new FrameworkStrategy("netstandard1.5", ".NET Standard 1.5 compatible", "netcoreapp2.0") {
+ };
+ static FrameworkStrategy NETSTANDARD_1_6 = new FrameworkStrategy("netstandard1.6", ".NET Standard 1.6 compatible", "netcoreapp2.0") {
+ };
+ static FrameworkStrategy NETSTANDARD_2_0 = new FrameworkStrategy("netstandard2.0", ".NET Standard 2.0 compatible", "netcoreapp2.0") {
+ };
+ static FrameworkStrategy NETSTANDARD_2_1 = new FrameworkStrategy("netstandard2.1", ".NET Standard 2.1 compatible", "netcoreapp3.0") {
+ };
+ static FrameworkStrategy NETCOREAPP_2_0 = new FrameworkStrategy("netcoreapp2.0", ".NET Core 2.0 compatible", "netcoreapp2.0", Boolean.FALSE) {
+ };
+ static FrameworkStrategy NETCOREAPP_2_1 = new FrameworkStrategy("netcoreapp2.1", ".NET Core 2.1 compatible", "netcoreapp2.1", Boolean.FALSE) {
+ };
+ static FrameworkStrategy NETCOREAPP_3_0 = new FrameworkStrategy("netcoreapp3.0", ".NET Core 3.0 compatible", "netcoreapp3.0", Boolean.FALSE) {
+ };
+ static FrameworkStrategy NETCOREAPP_3_1 = new FrameworkStrategy("netcoreapp3.1", ".NET Core 3.1 compatible", "netcoreapp3.1", Boolean.FALSE) {
+ };
+ static FrameworkStrategy NETFRAMEWORK_4_7 = new FrameworkStrategy("net47", ".NET Framework 4.7 compatible", "net47", Boolean.FALSE) {
+ };
+ static FrameworkStrategy NET_5_0 = new FrameworkStrategy("net5.0", ".NET 5.0 compatible", "net5.0", Boolean.FALSE) {
+ };
+ protected String name;
+ protected String description;
+ protected String testTargetFramework;
+ private Boolean isNetStandard = Boolean.TRUE;
+
+ FrameworkStrategy(String name, String description, String testTargetFramework) {
+ this.name = name;
+ this.description = description;
+ this.testTargetFramework = testTargetFramework;
+ }
+
+ FrameworkStrategy(String name, String description, String testTargetFramework, Boolean isNetStandard) {
+ this.name = name;
+ this.description = description;
+ this.testTargetFramework = testTargetFramework;
+ this.isNetStandard = isNetStandard;
+ }
+
+ protected void configureAdditionalProperties(final Map properties) {
+ properties.putIfAbsent(CodegenConstants.DOTNET_FRAMEWORK, this.name);
+
+ // not intended to be user-settable
+ properties.put(TARGET_FRAMEWORK_IDENTIFIER, this.getTargetFrameworkIdentifier());
+ properties.put(TARGET_FRAMEWORK_VERSION, this.getTargetFrameworkVersion());
+ properties.putIfAbsent(MCS_NET_VERSION_KEY, "4.6-api");
+
+ properties.put(NET_STANDARD, this.isNetStandard);
+ if (properties.containsKey(SUPPORTS_UWP)) {
+ LOGGER.warn(".NET {} generator does not support the UWP option. Use the csharp generator instead.",
+ this.name);
+ properties.remove(SUPPORTS_UWP);
+ }
+ }
+
+ protected String getNugetFrameworkIdentifier() {
+ return this.name.toLowerCase(Locale.ROOT);
+ }
+
+ protected String getTargetFrameworkIdentifier() {
+ if (this.isNetStandard) return ".NETStandard";
+ else return ".NETCoreApp";
+ }
+
+ protected String getTargetFrameworkVersion() {
+ if (this.isNetStandard) return "v" + this.name.replace("netstandard", "");
+ else return "v" + this.name.replace("netcoreapp", "");
+ }
+ }
+
+ protected void configureAdditionalPropertiesForFrameworks(final Map properties, List strategies) {
+ properties.putIfAbsent(CodegenConstants.DOTNET_FRAMEWORK, strategies.stream()
+ .map(p -> p.name)
+ .collect(Collectors.joining(";")));
+
+ // not intended to be user-settable
+ properties.put(TARGET_FRAMEWORK_IDENTIFIER, strategies.stream()
+ .map(p -> p.getTargetFrameworkIdentifier())
+ .collect(Collectors.joining(";")));
+ properties.put(TARGET_FRAMEWORK_VERSION, strategies.stream()
+ .map(p -> p.getTargetFrameworkVersion())
+ .collect(Collectors.joining(";")));
+ properties.putIfAbsent(MCS_NET_VERSION_KEY, "4.6-api");
+
+ properties.put(NET_STANDARD, strategies.stream().anyMatch(p -> Boolean.TRUE.equals(p.isNetStandard)));
+ }
+
+ /**
+ * Return the instantiation type of the property, especially for map and array
+ *
+ * @param schema property schema
+ * @return string presentation of the instantiation type of the property
+ */
+ @Override
+ public String toInstantiationType(Schema schema) {
+ if (ModelUtils.isMapSchema(schema)) {
+ Schema additionalProperties = getAdditionalProperties(schema);
+ String inner = getSchemaType(additionalProperties);
+ if (ModelUtils.isMapSchema(additionalProperties)) {
+ inner = toInstantiationType(additionalProperties);
+ }
+ return instantiationTypes.get("map") + "";
+ } else if (ModelUtils.isArraySchema(schema)) {
+ ArraySchema arraySchema = (ArraySchema) schema;
+ String inner = getSchemaType(arraySchema.getItems());
+ return instantiationTypes.get("array") + "<" + inner + ">";
+ } else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map postProcessModels(Map objs) {
+ objs = super.postProcessModels(objs);
+ List models = (List) objs.get("models");
+
+ // add implements for serializable/parcelable to all models
+ for (Object _mo : models) {
+ Map mo = (Map) _mo;
+ CodegenModel cm = (CodegenModel) mo.get("model");
+
+ if (cm.oneOf != null && !cm.oneOf.isEmpty() && cm.oneOf.contains("ModelNull")) {
+ // if oneOf contains "null" type
+ cm.isNullable = true;
+ cm.oneOf.remove("ModelNull");
+ }
+
+ if (cm.anyOf != null && !cm.anyOf.isEmpty() && cm.anyOf.contains("ModelNull")) {
+ // if anyOf contains "null" type
+ cm.isNullable = true;
+ cm.anyOf.remove("ModelNull");
+ }
+ }
+
+ return objs;
+ }
+
+ @Override
+ public void postProcess() {
+ System.out.println("################################################################################");
+ System.out.println("# Thanks for using OpenAPI Generator. #");
+ System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F #");
+ System.out.println("# https://opencollective.com/openapi_generator/donate #");
+ System.out.println("# #");
+ System.out.println("# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#");
+ System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #");
+ System.out.println("################################################################################");
+ }
+
+ @Override
+ protected void updateModelForObject(CodegenModel m, Schema schema) {
+ /**
+ * we have a custom version of this function so we only set isMap to true if
+ * ModelUtils.isMapSchema
+ * In other generators, isMap is true for all type object schemas
+ */
+ if (schema.getProperties() != null || schema.getRequired() != null && !(schema instanceof ComposedSchema)) {
+ // passing null to allProperties and allRequired as there's no parent
+ addVars(m, unaliasPropertySchema(schema.getProperties()), schema.getRequired(), null, null);
+ }
+ if (ModelUtils.isMapSchema(schema)) {
+ // an object or anyType composed schema that has additionalProperties set
+ addAdditionPropertiesToCodeGenModel(m, schema);
+ } else {
+ m.setIsMap(false);
+ if (ModelUtils.isFreeFormObject(openAPI, schema)) {
+ // non-composed object type with no properties + additionalProperties
+ // additionalProperties must be null, ObjectSchema, or empty Schema
+ addAdditionPropertiesToCodeGenModel(m, schema);
+ }
+ }
+ // process 'additionalProperties'
+ setAddProps(schema, m);
+ }
+}
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
new file mode 100644
index 00000000000..fb99c5a149a
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CsharpNetcoreFunctionsServerCodegen.java
@@ -0,0 +1,82 @@
+package org.openapitools.codegen.languages;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CsharpNetcoreFunctionsServerCodegen extends CSharpNetCoreReducedClientCodegen {
+ public static final String PROJECT_NAME = "projectName";
+
+ final Logger LOGGER = LoggerFactory.getLogger(CsharpNetcoreFunctionsServerCodegen.class);
+
+ public CodegenType getTag() {
+ return CodegenType.SERVER;
+ }
+
+ public String getName() {
+ return "csharp-netcore-functions";
+ }
+
+ public String getHelp() {
+ return "Generates a csharp server.";
+ }
+
+ public CsharpNetcoreFunctionsServerCodegen() {
+ super();
+ 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"));
+
+ 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"));
+ }
+
+ @Override
+ public String apiFileFolder() {
+ return outputFolder + File.separator + "generatedSrc" + File.separator + "Functions";
+ }
+
+ @Override
+ public String modelFileFolder() {
+ return outputFolder + File.separator + "generatedSrc" + File.separator + "Models";
+ }
+
+ @Override
+ public String apiDocFileFolder() {
+ return (outputFolder + "/Docs").replace('/', File.separatorChar);
+ }
+
+ @Override
+ public String apiTestFileFolder() {
+ return outputFolder + File.separator + "Tests" + File.separator + "Tests" + File.separator + apiPackage();
+ }
+
+}
diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
index 78855ce50ad..727ec5d990f 100644
--- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
+++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig
@@ -23,6 +23,7 @@ org.openapitools.codegen.languages.CSharpClientCodegen
org.openapitools.codegen.languages.CSharpNetCoreClientCodegen
org.openapitools.codegen.languages.CSharpDotNet2ClientCodegen
org.openapitools.codegen.languages.CSharpNancyFXServerCodegen
+org.openapitools.codegen.languages.CsharpNetcoreFunctionsServerCodegen
org.openapitools.codegen.languages.DartClientCodegen
org.openapitools.codegen.languages.DartDioClientCodegen
org.openapitools.codegen.languages.DartDioNextClientCodegen
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AbstractOpenAPISchema.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AbstractOpenAPISchema.mustache
new file mode 100644
index 00000000000..05e78204959
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AbstractOpenAPISchema.mustache
@@ -0,0 +1,68 @@
+{{>partial_header}}
+
+using System;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace {{packageName}}.{{modelPackage}}
+{
+ ///
+ /// Abstract base class for oneOf, anyOf schemas in the OpenAPI specification
+ ///
+ {{>visibility}} abstract partial class AbstractOpenAPISchema
+ {
+ ///
+ /// Custom JSON serializer
+ ///
+ static public readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ MissingMemberHandling = MissingMemberHandling.Error,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ ///
+ /// Custom JSON serializer for objects with additional properties
+ ///
+ static public readonly JsonSerializerSettings AdditionalPropertiesSerializerSettings = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ MissingMemberHandling = MissingMemberHandling.Ignore,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ ///
+ /// Gets or Sets the actual instance
+ ///
+ public abstract Object ActualInstance { get; set; }
+
+ ///
+ /// Gets or Sets IsNullable to indicate whether the instance is nullable
+ ///
+ public bool IsNullable { get; protected set; }
+
+ ///
+ /// Gets or Sets the schema type, which can be either `oneOf` or `anyOf`
+ ///
+ public string SchemaType { get; protected set; }
+
+ ///
+ /// Converts the instance into JSON string.
+ ///
+ public abstract string ToJson();
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiClient.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiClient.mustache
new file mode 100644
index 00000000000..65cbf8d76bc
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiClient.mustache
@@ -0,0 +1,867 @@
+{{>partial_header}}
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters;
+using System.Text;
+using System.Threading;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+{{^netStandard}}
+using System.Web;
+{{/netStandard}}
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
+using RestSharp;
+using RestSharp.Deserializers;
+using RestSharpMethod = RestSharp.Method;
+{{#useWebRequest}}
+using System.Net.Http;
+{{/useWebRequest}}
+{{#supportsRetry}}
+using Polly;
+{{/supportsRetry}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Allows RestSharp to Serialize/Deserialize JSON using our custom logic, but only when ContentType is JSON.
+ ///
+ internal class CustomJsonCodec : RestSharp.Serializers.ISerializer, RestSharp.Deserializers.IDeserializer
+ {
+ private readonly IReadableConfiguration _configuration;
+ private static readonly string _contentType = "application/json";
+ private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ public CustomJsonCodec(IReadableConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public CustomJsonCodec(JsonSerializerSettings serializerSettings, IReadableConfiguration configuration)
+ {
+ _serializerSettings = serializerSettings;
+ _configuration = configuration;
+ }
+
+ ///
+ /// Serialize the object into a JSON string.
+ ///
+ /// Object to be serialized.
+ /// A JSON string.
+ public string Serialize(object obj)
+ {
+ if (obj != null && obj is {{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema)
+ {
+ // the object to be serialized is an oneOf/anyOf schema
+ return (({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema)obj).ToJson();
+ }
+ else
+ {
+ return JsonConvert.SerializeObject(obj, _serializerSettings);
+ }
+ }
+
+ public T Deserialize(IRestResponse response)
+ {
+ var result = (T)Deserialize(response, typeof(T));
+ return result;
+ }
+
+ ///
+ /// Deserialize the JSON string into a proper object.
+ ///
+ /// The HTTP response.
+ /// Object type.
+ /// Object representation of the JSON string.
+ internal object Deserialize(IRestResponse response, Type type)
+ {
+ if (type == typeof(byte[])) // return byte array
+ {
+ return response.RawBytes;
+ }
+
+ // TODO: ? if (type.IsAssignableFrom(typeof(Stream)))
+ if (type == typeof(Stream))
+ {
+ var bytes = response.RawBytes;
+ if (response.Headers != null)
+ {
+ var filePath = string.IsNullOrEmpty(_configuration.TempFolderPath)
+ ? Path.GetTempPath()
+ : _configuration.TempFolderPath;
+ var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$");
+ foreach (var header in response.Headers)
+ {
+ var match = regex.Match(header.ToString());
+ if (match.Success)
+ {
+ string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
+ File.WriteAllBytes(fileName, bytes);
+ return new FileStream(fileName, FileMode.Open);
+ }
+ }
+ }
+ var stream = new MemoryStream(bytes);
+ return stream;
+ }
+
+ if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
+ {
+ return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind);
+ }
+
+ if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
+ {
+ return Convert.ChangeType(response.Content, type);
+ }
+
+ // at this point, it must be a model (json)
+ try
+ {
+ return JsonConvert.DeserializeObject(response.Content, type, _serializerSettings);
+ }
+ catch (Exception e)
+ {
+ throw new ApiException(500, e.Message);
+ }
+ }
+
+ public string RootElement { get; set; }
+ public string Namespace { get; set; }
+ public string DateFormat { get; set; }
+
+ public string ContentType
+ {
+ get { return _contentType; }
+ set { throw new InvalidOperationException("Not allowed to set content type."); }
+ }
+ }
+ {{! NOTE: Any changes related to RestSharp should be done in this class. All other client classes are for extensibility by consumers.}}
+ ///
+ /// Provides a default implementation of an Api client (both synchronous and asynchronous implementatios),
+ /// encapsulating general REST accessor use cases.
+ ///
+ {{>visibility}} partial class ApiClient : ISynchronousClient{{#supportsAsync}}, IAsynchronousClient{{/supportsAsync}}
+ {
+ private readonly string _baseUrl;
+
+ ///
+ /// Specifies the settings on a object.
+ /// These settings can be adjusted to accomodate custom serialization rules.
+ ///
+ public JsonSerializerSettings SerializerSettings { get; set; } = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ ///
+ /// Allows for extending request processing for generated code.
+ ///
+ /// The RestSharp request object
+ partial void InterceptRequest(IRestRequest request);
+
+ ///
+ /// Allows for extending response processing for generated code.
+ ///
+ /// The RestSharp request object
+ /// The RestSharp response object
+ partial void InterceptResponse(IRestRequest request, IRestResponse response);
+
+ ///
+ /// Initializes a new instance of the , defaulting to the global configurations' base url.
+ ///
+ public ApiClient()
+ {
+ _baseUrl = {{packageName}}.Client.GlobalConfiguration.Instance.BasePath;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ ///
+ /// The target service's base path in URL format.
+ ///
+ public ApiClient(string basePath)
+ {
+ if (string.IsNullOrEmpty(basePath))
+ throw new ArgumentException("basePath cannot be empty");
+
+ _baseUrl = basePath;
+ }
+
+ ///
+ /// Constructs the RestSharp version of an http method
+ ///
+ /// Swagger Client Custom HttpMethod
+ /// RestSharp's HttpMethod instance.
+ ///
+ private RestSharpMethod Method(HttpMethod method)
+ {
+ RestSharpMethod other;
+ switch (method)
+ {
+ case HttpMethod.Get:
+ other = RestSharpMethod.GET;
+ break;
+ case HttpMethod.Post:
+ other = RestSharpMethod.POST;
+ break;
+ case HttpMethod.Put:
+ other = RestSharpMethod.PUT;
+ break;
+ case HttpMethod.Delete:
+ other = RestSharpMethod.DELETE;
+ break;
+ case HttpMethod.Head:
+ other = RestSharpMethod.HEAD;
+ break;
+ case HttpMethod.Options:
+ other = RestSharpMethod.OPTIONS;
+ break;
+ case HttpMethod.Patch:
+ other = RestSharpMethod.PATCH;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("method", method, null);
+ }
+
+ return other;
+ }
+
+ ///
+ /// Provides all logic for constructing a new RestSharp .
+ /// At this point, all information for querying the service is known. Here, it is simply
+ /// mapped into the RestSharp request.
+ ///
+ /// The http verb.
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// [private] A new RestRequest instance.
+ ///
+ private RestRequest NewRequest(
+ HttpMethod method,
+ string path,
+ RequestOptions options,
+ IReadableConfiguration configuration)
+ {
+ if (path == null) throw new ArgumentNullException("path");
+ if (options == null) throw new ArgumentNullException("options");
+ if (configuration == null) throw new ArgumentNullException("configuration");
+
+ RestRequest request = new RestRequest(Method(method))
+ {
+ Resource = path,
+ JsonSerializer = new CustomJsonCodec(SerializerSettings, configuration)
+ };
+
+ if (options.PathParameters != null)
+ {
+ foreach (var pathParam in options.PathParameters)
+ {
+ request.AddParameter(pathParam.Key, pathParam.Value, ParameterType.UrlSegment);
+ }
+ }
+
+ if (options.QueryParameters != null)
+ {
+ foreach (var queryParam in options.QueryParameters)
+ {
+ foreach (var value in queryParam.Value)
+ {
+ request.AddQueryParameter(queryParam.Key, value);
+ }
+ }
+ }
+
+ if (configuration.DefaultHeaders != null)
+ {
+ foreach (var headerParam in configuration.DefaultHeaders)
+ {
+ request.AddHeader(headerParam.Key, headerParam.Value);
+ }
+ }
+
+ if (options.HeaderParameters != null)
+ {
+ foreach (var headerParam in options.HeaderParameters)
+ {
+ foreach (var value in headerParam.Value)
+ {
+ request.AddHeader(headerParam.Key, value);
+ }
+ }
+ }
+
+ if (options.FormParameters != null)
+ {
+ foreach (var formParam in options.FormParameters)
+ {
+ request.AddParameter(formParam.Key, formParam.Value);
+ }
+ }
+
+ if (options.Data != null)
+ {
+ if (options.Data is Stream stream)
+ {
+ var contentType = "application/octet-stream";
+ if (options.HeaderParameters != null)
+ {
+ var contentTypes = options.HeaderParameters["Content-Type"];
+ contentType = contentTypes[0];
+ }
+
+ var bytes = ClientUtils.ReadAsBytes(stream);
+ request.AddParameter(contentType, bytes, ParameterType.RequestBody);
+ }
+ else
+ {
+ if (options.HeaderParameters != null)
+ {
+ var contentTypes = options.HeaderParameters["Content-Type"];
+ if (contentTypes == null || contentTypes.Any(header => header.Contains("application/json")))
+ {
+ request.RequestFormat = DataFormat.Json;
+ }
+ else
+ {
+ // TODO: Generated client user should add additional handlers. RestSharp only supports XML and JSON, with XML as default.
+ }
+ }
+ else
+ {
+ // Here, we'll assume JSON APIs are more common. XML can be forced by adding produces/consumes to openapi spec explicitly.
+ request.RequestFormat = DataFormat.Json;
+ }
+
+ request.AddJsonBody(options.Data);
+ }
+ }
+
+ if (options.FileParameters != null)
+ {
+ foreach (var fileParam in options.FileParameters)
+ {
+ var bytes = ClientUtils.ReadAsBytes(fileParam.Value);
+ var fileStream = fileParam.Value as FileStream;
+ if (fileStream != null)
+ request.Files.Add(FileParameter.Create(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)));
+ else
+ request.Files.Add(FileParameter.Create(fileParam.Key, bytes, "no_file_name_provided"));
+ }
+ }
+
+ if (options.Cookies != null && options.Cookies.Count > 0)
+ {
+ foreach (var cookie in options.Cookies)
+ {
+ request.AddCookie(cookie.Name, cookie.Value);
+ }
+ }
+
+ return request;
+ }
+
+ private ApiResponse ToApiResponse(IRestResponse response)
+ {
+ T result = response.Data;
+ string rawContent = response.Content;
+
+ var transformed = new ApiResponse(response.StatusCode, new Multimap({{#caseInsensitiveResponseHeaders}}StringComparer.OrdinalIgnoreCase{{/caseInsensitiveResponseHeaders}}), result, rawContent)
+ {
+ ErrorText = response.ErrorMessage,
+ Cookies = new List()
+ };
+
+ if (response.Headers != null)
+ {
+ foreach (var responseHeader in response.Headers)
+ {
+ transformed.Headers.Add(responseHeader.Name, ClientUtils.ParameterToString(responseHeader.Value));
+ }
+ }
+
+ if (response.Cookies != null)
+ {
+ foreach (var responseCookies in response.Cookies)
+ {
+ transformed.Cookies.Add(
+ new Cookie(
+ responseCookies.Name,
+ responseCookies.Value,
+ responseCookies.Path,
+ responseCookies.Domain)
+ );
+ }
+ }
+
+ return transformed;
+ }
+
+ private ApiResponse Exec(RestRequest req, IReadableConfiguration configuration)
+ {
+ RestClient client = new RestClient(_baseUrl);
+
+ client.ClearHandlers();
+ var existingDeserializer = req.JsonSerializer as IDeserializer;
+ if (existingDeserializer != null)
+ {
+ client.AddHandler("application/json", () => existingDeserializer);
+ client.AddHandler("text/json", () => existingDeserializer);
+ client.AddHandler("text/x-json", () => existingDeserializer);
+ client.AddHandler("text/javascript", () => existingDeserializer);
+ client.AddHandler("*+json", () => existingDeserializer);
+ }
+ else
+ {
+ var customDeserializer = new CustomJsonCodec(SerializerSettings, configuration);
+ client.AddHandler("application/json", () => customDeserializer);
+ client.AddHandler("text/json", () => customDeserializer);
+ client.AddHandler("text/x-json", () => customDeserializer);
+ client.AddHandler("text/javascript", () => customDeserializer);
+ client.AddHandler("*+json", () => customDeserializer);
+ }
+
+ var xmlDeserializer = new XmlDeserializer();
+ client.AddHandler("application/xml", () => xmlDeserializer);
+ client.AddHandler("text/xml", () => xmlDeserializer);
+ client.AddHandler("*+xml", () => xmlDeserializer);
+ client.AddHandler("*", () => xmlDeserializer);
+
+ client.Timeout = configuration.Timeout;
+
+ if (configuration.Proxy != null)
+ {
+ client.Proxy = configuration.Proxy;
+ }
+
+ if (configuration.UserAgent != null)
+ {
+ client.UserAgent = configuration.UserAgent;
+ }
+
+ if (configuration.ClientCertificates != null)
+ {
+ client.ClientCertificates = configuration.ClientCertificates;
+ }
+
+ InterceptRequest(req);
+
+ IRestResponse response;
+ if (RetryConfiguration.RetryPolicy != null)
+ {
+ var policy = RetryConfiguration.RetryPolicy;
+ var policyResult = policy.ExecuteAndCapture(() => client.Execute(req));
+ response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize(policyResult.Result) : new RestResponse
+ {
+ Request = req,
+ ErrorException = policyResult.FinalException
+ };
+ }
+ else
+ {
+ response = client.Execute(req);
+ }
+
+ // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
+ if (typeof({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
+ {
+ response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
+ }
+ else if (typeof(T).Name == "Stream") // for binary response
+ {
+ response.Data = (T)(object)new MemoryStream(response.RawBytes);
+ }
+
+ InterceptResponse(req, response);
+
+ var result = ToApiResponse(response);
+ if (response.ErrorMessage != null)
+ {
+ result.ErrorText = response.ErrorMessage;
+ }
+
+ if (response.Cookies != null && response.Cookies.Count > 0)
+ {
+ if (result.Cookies == null) result.Cookies = new List();
+ foreach (var restResponseCookie in response.Cookies)
+ {
+ var cookie = new Cookie(
+ restResponseCookie.Name,
+ restResponseCookie.Value,
+ restResponseCookie.Path,
+ restResponseCookie.Domain
+ )
+ {
+ Comment = restResponseCookie.Comment,
+ CommentUri = restResponseCookie.CommentUri,
+ Discard = restResponseCookie.Discard,
+ Expired = restResponseCookie.Expired,
+ Expires = restResponseCookie.Expires,
+ HttpOnly = restResponseCookie.HttpOnly,
+ Port = restResponseCookie.Port,
+ Secure = restResponseCookie.Secure,
+ Version = restResponseCookie.Version
+ };
+
+ result.Cookies.Add(cookie);
+ }
+ }
+ return result;
+ }
+
+ {{#supportsAsync}}
+ private async Task> ExecAsync(RestRequest req, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ RestClient client = new RestClient(_baseUrl);
+
+ client.ClearHandlers();
+ var existingDeserializer = req.JsonSerializer as IDeserializer;
+ if (existingDeserializer != null)
+ {
+ client.AddHandler("application/json", () => existingDeserializer);
+ client.AddHandler("text/json", () => existingDeserializer);
+ client.AddHandler("text/x-json", () => existingDeserializer);
+ client.AddHandler("text/javascript", () => existingDeserializer);
+ client.AddHandler("*+json", () => existingDeserializer);
+ }
+ else
+ {
+ var customDeserializer = new CustomJsonCodec(SerializerSettings, configuration);
+ client.AddHandler("application/json", () => customDeserializer);
+ client.AddHandler("text/json", () => customDeserializer);
+ client.AddHandler("text/x-json", () => customDeserializer);
+ client.AddHandler("text/javascript", () => customDeserializer);
+ client.AddHandler("*+json", () => customDeserializer);
+ }
+
+ var xmlDeserializer = new XmlDeserializer();
+ client.AddHandler("application/xml", () => xmlDeserializer);
+ client.AddHandler("text/xml", () => xmlDeserializer);
+ client.AddHandler("*+xml", () => xmlDeserializer);
+ client.AddHandler("*", () => xmlDeserializer);
+
+ client.Timeout = configuration.Timeout;
+
+ if (configuration.Proxy != null)
+ {
+ client.Proxy = configuration.Proxy;
+ }
+
+ if (configuration.UserAgent != null)
+ {
+ client.UserAgent = configuration.UserAgent;
+ }
+
+ if (configuration.ClientCertificates != null)
+ {
+ client.ClientCertificates = configuration.ClientCertificates;
+ }
+
+ InterceptRequest(req);
+
+ IRestResponse response;
+ {{#supportsRetry}}
+ if (RetryConfiguration.AsyncRetryPolicy != null)
+ {
+ var policy = RetryConfiguration.AsyncRetryPolicy;
+ var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(req, ct), cancellationToken).ConfigureAwait(false);
+ response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize(policyResult.Result) : new RestResponse
+ {
+ Request = req,
+ ErrorException = policyResult.FinalException
+ };
+ }
+ else
+ {
+ {{/supportsRetry}}
+ response = await client.ExecuteAsync(req, cancellationToken).ConfigureAwait(false);
+ {{#supportsRetry}}
+ }
+ {{/supportsRetry}}
+
+ // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
+ if (typeof({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
+ {
+ response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
+ }
+ else if (typeof(T).Name == "Stream") // for binary response
+ {
+ response.Data = (T)(object)new MemoryStream(response.RawBytes);
+ }
+
+ InterceptResponse(req, response);
+
+ var result = ToApiResponse(response);
+ if (response.ErrorMessage != null)
+ {
+ result.ErrorText = response.ErrorMessage;
+ }
+
+ if (response.Cookies != null && response.Cookies.Count > 0)
+ {
+ if (result.Cookies == null) result.Cookies = new List();
+ foreach (var restResponseCookie in response.Cookies)
+ {
+ var cookie = new Cookie(
+ restResponseCookie.Name,
+ restResponseCookie.Value,
+ restResponseCookie.Path,
+ restResponseCookie.Domain
+ )
+ {
+ Comment = restResponseCookie.Comment,
+ CommentUri = restResponseCookie.CommentUri,
+ Discard = restResponseCookie.Discard,
+ Expired = restResponseCookie.Expired,
+ Expires = restResponseCookie.Expires,
+ HttpOnly = restResponseCookie.HttpOnly,
+ Port = restResponseCookie.Port,
+ Secure = restResponseCookie.Secure,
+ Version = restResponseCookie.Version
+ };
+
+ result.Cookies.Add(cookie);
+ }
+ }
+ return result;
+ }
+
+ #region IAsynchronousClient
+ ///
+ /// Make a HTTP GET request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> GetAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Get, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP POST request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> PostAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Post, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP PUT request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> PutAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Put, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP DELETE request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> DeleteAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Delete, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP HEAD request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> HeadAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Head, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP OPTION request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> OptionsAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Options, path, options, config), config, cancellationToken);
+ }
+
+ ///
+ /// Make a HTTP PATCH request (async).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// Token that enables callers to cancel the request.
+ /// A Task containing ApiResponse
+ public Task> PatchAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return ExecAsync(NewRequest(HttpMethod.Patch, path, options, config), config, cancellationToken);
+ }
+ #endregion IAsynchronousClient
+ {{/supportsAsync}}
+
+ #region ISynchronousClient
+ ///
+ /// Make a HTTP GET request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Get(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Get, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP POST request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Post(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Post, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP PUT request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Put(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Put, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP DELETE request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Delete(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Delete, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP HEAD request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Head(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Head, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP OPTION request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Options(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Options, path, options, config), config);
+ }
+
+ ///
+ /// Make a HTTP PATCH request (synchronous).
+ ///
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// A Task containing ApiResponse
+ public ApiResponse Patch(string path, RequestOptions options, IReadableConfiguration configuration = null)
+ {
+ var config = configuration ?? GlobalConfiguration.Instance;
+ return Exec(NewRequest(HttpMethod.Patch, path, options, config), config);
+ }
+ #endregion ISynchronousClient
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiException.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiException.mustache
new file mode 100644
index 00000000000..f7dadc5573c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiException.mustache
@@ -0,0 +1,60 @@
+{{>partial_header}}
+
+using System;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// API Exception
+ ///
+ {{>visibility}} class ApiException : Exception
+ {
+ ///
+ /// Gets or sets the error code (HTTP status code)
+ ///
+ /// The error code (HTTP status code).
+ public int ErrorCode { get; set; }
+
+ ///
+ /// Gets or sets the error content (body json object)
+ ///
+ /// The error content (Http response body).
+ public object ErrorContent { get; private set; }
+
+ ///
+ /// Gets or sets the HTTP headers
+ ///
+ /// HTTP headers
+ public Multimap Headers { get; private set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ApiException() { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// Error message.
+ public ApiException(int errorCode, string message) : base(message)
+ {
+ this.ErrorCode = errorCode;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// Error message.
+ /// Error content.
+ /// HTTP Headers.
+ public ApiException(int errorCode, string message, object errorContent = null, Multimap headers = null) : base(message)
+ {
+ this.ErrorCode = errorCode;
+ this.ErrorContent = errorContent;
+ this.Headers = headers;
+ }
+ }
+
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiResponse.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiResponse.mustache
new file mode 100644
index 00000000000..161c2acd16c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ApiResponse.mustache
@@ -0,0 +1,158 @@
+{{>partial_header}}
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Provides a non-generic contract for the ApiResponse wrapper.
+ ///
+ public interface IApiResponse
+ {
+ ///
+ /// The data type of
+ ///
+ Type ResponseType { get; }
+
+ ///
+ /// The content of this response
+ ///
+ Object Content { get; }
+
+ ///
+ /// Gets or sets the status code (HTTP status code)
+ ///
+ /// The status code.
+ HttpStatusCode StatusCode { get; }
+
+ ///
+ /// Gets or sets the HTTP headers
+ ///
+ /// HTTP headers
+ Multimap Headers { get; }
+
+ ///
+ /// Gets or sets any error text defined by the calling client.
+ ///
+ string ErrorText { get; set; }
+
+ ///
+ /// Gets or sets any cookies passed along on the response.
+ ///
+ List Cookies { get; set; }
+
+ ///
+ /// The raw content of this response
+ ///
+ string RawContent { get; }
+ }
+
+ ///
+ /// API Response
+ ///
+ public class ApiResponse : IApiResponse
+ {
+ #region Properties
+
+ ///
+ /// Gets or sets the status code (HTTP status code)
+ ///
+ /// The status code.
+ public HttpStatusCode StatusCode { get; }
+
+ ///
+ /// Gets or sets the HTTP headers
+ ///
+ /// HTTP headers
+ public Multimap Headers { get; }
+
+ ///
+ /// Gets or sets the data (parsed HTTP body)
+ ///
+ /// The data.
+ public T Data { get; }
+
+ ///
+ /// Gets or sets any error text defined by the calling client.
+ ///
+ public string ErrorText { get; set; }
+
+ ///
+ /// Gets or sets any cookies passed along on the response.
+ ///
+ public List Cookies { get; set; }
+
+ ///
+ /// The content of this response
+ ///
+ public Type ResponseType
+ {
+ get { return typeof(T); }
+ }
+
+ ///
+ /// The data type of
+ ///
+ public object Content
+ {
+ get { return Data; }
+ }
+
+ ///
+ /// The raw content
+ ///
+ public string RawContent { get; }
+
+ #endregion Properties
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// HTTP headers.
+ /// Data (parsed HTTP body)
+ /// Raw content.
+ public ApiResponse(HttpStatusCode statusCode, Multimap headers, T data, string rawContent)
+ {
+ StatusCode = statusCode;
+ Headers = headers;
+ Data = data;
+ RawContent = rawContent;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// HTTP headers.
+ /// Data (parsed HTTP body)
+ public ApiResponse(HttpStatusCode statusCode, Multimap headers, T data) : this(statusCode, headers, data, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// Data (parsed HTTP body)
+ /// Raw content.
+ public ApiResponse(HttpStatusCode statusCode, T data, string rawContent) : this(statusCode, null, data, rawContent)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// HTTP status code.
+ /// Data (parsed HTTP body)
+ public ApiResponse(HttpStatusCode statusCode, T data) : this(statusCode, data, null)
+ {
+ }
+
+ #endregion Constructors
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AssemblyInfo.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AssemblyInfo.mustache
new file mode 100644
index 00000000000..d5d937dc19f
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/AssemblyInfo.mustache
@@ -0,0 +1,40 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("{{packageTitle}}")]
+[assembly: AssemblyDescription("{{packageDescription}}")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("{{packageCompany}}")]
+[assembly: AssemblyProduct("{{packageProductName}}")]
+[assembly: AssemblyCopyright("{{packageCopyright}}")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("{{packageVersion}}")]
+[assembly: AssemblyFileVersion("{{packageVersion}}")]
+{{^supportsAsync}}
+// Settings which don't support asynchronous operations rely on non-public constructors
+// This is due to how RestSharp requires the type constraint `where T : new()` in places it probably shouldn't.
+[assembly: InternalsVisibleTo("RestSharp")]
+[assembly: InternalsVisibleTo("NewtonSoft.Json")]
+[assembly: InternalsVisibleTo("JsonSubTypes")]
+{{/supportsAsync}}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ClientUtils.mustache
new file mode 100644
index 00000000000..3c04dcdbd0c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ClientUtils.mustache
@@ -0,0 +1,239 @@
+{{>partial_header}}
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+{{#useCompareNetObjects}}
+using KellermanSoftware.CompareNetObjects;
+{{/useCompareNetObjects}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Utility functions providing some benefit to API client consumers.
+ ///
+ public static class ClientUtils
+ {
+ {{#useCompareNetObjects}}
+ ///
+ /// An instance of CompareLogic.
+ ///
+ public static CompareLogic compareLogic;
+
+ ///
+ /// Static contstructor to initialise compareLogic.
+ ///
+ static ClientUtils()
+ {
+ compareLogic = new CompareLogic();
+ }
+
+ {{/useCompareNetObjects}}
+ ///
+ /// Sanitize filename by removing the path
+ ///
+ /// Filename
+ /// Filename
+ public static string SanitizeFilename(string filename)
+ {
+ Match match = Regex.Match(filename, @".*[/\\](.*)$");
+ return match.Success ? match.Groups[1].Value : filename;
+ }
+
+ ///
+ /// Convert params to key/value pairs.
+ /// Use collectionFormat to properly format lists and collections.
+ ///
+ /// The swagger-supported collection format, one of: csv, tsv, ssv, pipes, multi
+ /// Key name.
+ /// Value object.
+ /// A multimap of keys with 1..n associated values.
+ public static Multimap ParameterToMultiMap(string collectionFormat, string name, object value)
+ {
+ var parameters = new Multimap();
+
+ if (value is ICollection collection && collectionFormat == "multi")
+ {
+ foreach (var item in collection)
+ {
+ parameters.Add(name, ParameterToString(item));
+ }
+ }
+ else if (value is IDictionary dictionary)
+ {
+ if(collectionFormat == "deepObject") {
+ foreach (DictionaryEntry entry in dictionary)
+ {
+ parameters.Add(name + "[" + entry.Key + "]", ParameterToString(entry.Value));
+ }
+ }
+ else {
+ foreach (DictionaryEntry entry in dictionary)
+ {
+ parameters.Add(entry.Key.ToString(), ParameterToString(entry.Value));
+ }
+ }
+ }
+ else
+ {
+ parameters.Add(name, ParameterToString(value));
+ }
+
+ return parameters;
+ }
+
+ ///
+ /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime.
+ /// If parameter is a list, join the list with ",".
+ /// Otherwise just return the string.
+ ///
+ /// The parameter (header, path, query, form).
+ /// An optional configuration instance, providing formatting options used in processing.
+ /// Formatted string.
+ public static string ParameterToString(object obj, IReadableConfiguration configuration = null)
+ {
+ if (obj is DateTime dateTime)
+ // Return a formatted date string - Can be customized with Configuration.DateTimeFormat
+ // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o")
+ // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8
+ // For example: 2009-06-15T13:45:30.0000000
+ return dateTime.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat);
+ if (obj is DateTimeOffset dateTimeOffset)
+ // Return a formatted date string - Can be customized with Configuration.DateTimeFormat
+ // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o")
+ // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8
+ // For example: 2009-06-15T13:45:30.0000000
+ return dateTimeOffset.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat);
+ if (obj is bool boolean)
+ return boolean ? "true" : "false";
+ if (obj is ICollection collection)
+ return string.Join(",", collection.Cast());
+
+ return Convert.ToString(obj, CultureInfo.InvariantCulture);
+ }
+
+ ///
+ /// URL encode a string
+ /// Credit/Ref: https://github.com/restsharp/RestSharp/blob/master/RestSharp/Extensions/StringExtensions.cs#L50
+ ///
+ /// string to be URL encoded
+ /// Byte array
+ public static string UrlEncode(string input)
+ {
+ const int maxLength = 32766;
+
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (input.Length <= maxLength)
+ {
+ return Uri.EscapeDataString(input);
+ }
+
+ StringBuilder sb = new StringBuilder(input.Length * 2);
+ int index = 0;
+
+ while (index < input.Length)
+ {
+ int length = Math.Min(input.Length - index, maxLength);
+ string subString = input.Substring(index, length);
+
+ sb.Append(Uri.EscapeDataString(subString));
+ index += subString.Length;
+ }
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Encode string in base64 format.
+ ///
+ /// string to be encoded.
+ /// Encoded string.
+ public static string Base64Encode(string text)
+ {
+ return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text));
+ }
+
+ ///
+ /// Convert stream to byte array
+ ///
+ /// Input stream to be converted
+ /// Byte array
+ public static byte[] ReadAsBytes(Stream inputStream)
+ {
+ using (var ms = new MemoryStream())
+ {
+ inputStream.CopyTo(ms);
+ return ms.ToArray();
+ }
+ }
+
+ ///
+ /// Select the Content-Type header's value from the given content-type array:
+ /// if JSON type exists in the given array, use it;
+ /// otherwise use the first one defined in 'consumes'
+ ///
+ /// The Content-Type array to select from.
+ /// The Content-Type header to use.
+ public static string SelectHeaderContentType(string[] contentTypes)
+ {
+ if (contentTypes.Length == 0)
+ return null;
+
+ foreach (var contentType in contentTypes)
+ {
+ if (IsJsonMime(contentType))
+ return contentType;
+ }
+
+ return contentTypes[0]; // use the first content type specified in 'consumes'
+ }
+
+ ///
+ /// Select the Accept header's value from the given accepts array:
+ /// if JSON exists in the given array, use it;
+ /// otherwise use all of them (joining into a string)
+ ///
+ /// The accepts array to select from.
+ /// The Accept header to use.
+ public static string SelectHeaderAccept(string[] accepts)
+ {
+ if (accepts.Length == 0)
+ return null;
+
+ if (accepts.Contains("application/json", StringComparer.OrdinalIgnoreCase))
+ return "application/json";
+
+ return string.Join(",", accepts);
+ }
+
+ ///
+ /// Provides a case-insensitive check that a provided content type is a known JSON-like content type.
+ ///
+ public static readonly Regex JsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$");
+
+ ///
+ /// Check if the given MIME is a JSON MIME.
+ /// JSON MIME examples:
+ /// application/json
+ /// application/json; charset=UTF8
+ /// APPLICATION/JSON
+ /// application/vnd.company+json
+ ///
+ /// MIME
+ /// Returns True if MIME type is json.
+ public static bool IsJsonMime(string mime)
+ {
+ if (string.IsNullOrWhiteSpace(mime)) return false;
+
+ return JsonRegex.IsMatch(mime) || mime.Equals("application/json-patch+json");
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Configuration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Configuration.mustache
new file mode 100644
index 00000000000..ead56414441
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Configuration.mustache
@@ -0,0 +1,574 @@
+{{>partial_header}}
+
+using System;
+{{^net35}}
+using System.Collections.Concurrent;
+{{/net35}}
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Represents a set of configuration settings
+ ///
+ {{>visibility}} class Configuration : IReadableConfiguration
+ {
+ #region Constants
+
+ ///
+ /// Version of the package.
+ ///
+ /// Version of the package.
+ public const string Version = "{{packageVersion}}";
+
+ ///
+ /// Identifier for ISO 8601 DateTime Format
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 for more information.
+ // ReSharper disable once InconsistentNaming
+ public const string ISO8601_DATETIME_FORMAT = "o";
+
+ #endregion Constants
+
+ #region Static Members
+
+ ///
+ /// Default creation of exceptions for a given method name and response object
+ ///
+ public static readonly ExceptionFactory DefaultExceptionFactory = (methodName, response) =>
+ {
+ var status = (int)response.StatusCode;
+ if (status >= 400)
+ {
+ return new ApiException(status,
+ string.Format("Error calling {0}: {1}", methodName, response.RawContent),
+ response.RawContent, response.Headers);
+ }
+ {{^netStandard}}
+ if (status == 0)
+ {
+ return new ApiException(status,
+ string.Format("Error calling {0}: {1}", methodName, response.ErrorText), response.ErrorText);
+ }
+ {{/netStandard}}
+ return null;
+ };
+
+ #endregion Static Members
+
+ #region Private Members
+
+ ///
+ /// Defines the base path of the target API server.
+ /// Example: http://localhost:3000/v1/
+ ///
+ private string _basePath;
+
+ ///
+ /// Gets or sets the API key based on the authentication name.
+ /// This is the key and value comprising the "secret" for acessing an API.
+ ///
+ /// The API key.
+ private IDictionary _apiKey;
+
+ ///
+ /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name.
+ ///
+ /// The prefix of the API key.
+ private IDictionary _apiKeyPrefix;
+
+ private string _dateTimeFormat = ISO8601_DATETIME_FORMAT;
+ private string _tempFolderPath = Path.GetTempPath();
+ {{#servers.0}}
+
+ ///
+ /// Gets or sets the servers defined in the OpenAPI spec.
+ ///
+ /// The servers
+ private IList> _servers;
+ {{/servers.0}}
+ {{#hasHttpSignatureMethods}}
+
+ ///
+ /// HttpSigning configuration
+ ///
+ private HttpSigningConfiguration _HttpSigningConfiguration = null;
+ {{/hasHttpSignatureMethods}}
+ #endregion Private Members
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
+ public Configuration()
+ {
+ Proxy = null;
+ UserAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{packageVersion}}/csharp{{/httpUserAgent}}";
+ BasePath = "{{{basePath}}}";
+ DefaultHeaders = new {{^net35}}Concurrent{{/net35}}Dictionary();
+ ApiKey = new {{^net35}}Concurrent{{/net35}}Dictionary();
+ ApiKeyPrefix = new {{^net35}}Concurrent{{/net35}}Dictionary();
+ {{#servers}}
+ {{#-first}}
+ Servers = new List>()
+ {
+ {{/-first}}
+ {
+ new Dictionary {
+ {"url", "{{{url}}}"},
+ {"description", "{{{description}}}{{^description}}No description provided{{/description}}"},
+ {{#variables}}
+ {{#-first}}
+ {
+ "variables", new Dictionary {
+ {{/-first}}
+ {
+ "{{{name}}}", new Dictionary {
+ {"description", "{{{description}}}{{^description}}No description provided{{/description}}"},
+ {"default_value", "{{{defaultValue}}}"},
+ {{#enumValues}}
+ {{#-first}}
+ {
+ "enum_values", new List() {
+ {{/-first}}
+ "{{{.}}}"{{^-last}},{{/-last}}
+ {{#-last}}
+ }
+ }
+ {{/-last}}
+ {{/enumValues}}
+ }
+ }{{^-last}},{{/-last}}
+ {{#-last}}
+ }
+ }
+ {{/-last}}
+ {{/variables}}
+ }
+ }{{^-last}},{{/-last}}
+ {{#-last}}
+ };
+ {{/-last}}
+ {{/servers}}
+
+ // Setting Timeout has side effects (forces ApiClient creation).
+ Timeout = 100000;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
+ public Configuration(
+ IDictionary defaultHeaders,
+ IDictionary apiKey,
+ IDictionary apiKeyPrefix,
+ string basePath = "{{{basePath}}}") : this()
+ {
+ if (string.{{^net35}}IsNullOrWhiteSpace{{/net35}}{{#net35}}IsNullOrEmpty{{/net35}}(basePath))
+ throw new ArgumentException("The provided basePath is invalid.", "basePath");
+ if (defaultHeaders == null)
+ throw new ArgumentNullException("defaultHeaders");
+ if (apiKey == null)
+ throw new ArgumentNullException("apiKey");
+ if (apiKeyPrefix == null)
+ throw new ArgumentNullException("apiKeyPrefix");
+
+ BasePath = basePath;
+
+ foreach (var keyValuePair in defaultHeaders)
+ {
+ DefaultHeaders.Add(keyValuePair);
+ }
+
+ foreach (var keyValuePair in apiKey)
+ {
+ ApiKey.Add(keyValuePair);
+ }
+
+ foreach (var keyValuePair in apiKeyPrefix)
+ {
+ ApiKeyPrefix.Add(keyValuePair);
+ }
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ ///
+ /// Gets or sets the base path for API access.
+ ///
+ public virtual string BasePath {
+ get { return _basePath; }
+ set { _basePath = value; }
+ }
+
+ ///
+ /// Gets or sets the default header.
+ ///
+ [Obsolete("Use DefaultHeaders instead.")]
+ public virtual IDictionary DefaultHeader
+ {
+ get
+ {
+ return DefaultHeaders;
+ }
+ set
+ {
+ DefaultHeaders = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the default headers.
+ ///
+ public virtual IDictionary DefaultHeaders { get; set; }
+
+ ///
+ /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds.
+ ///
+ public virtual int Timeout { get; set; }
+
+ ///
+ /// Gets or sets the proxy
+ ///
+ /// Proxy.
+ public virtual WebProxy Proxy { get; set; }
+
+ ///
+ /// Gets or sets the HTTP user agent.
+ ///
+ /// Http user agent.
+ public virtual string UserAgent { get; set; }
+
+ ///
+ /// Gets or sets the username (HTTP basic authentication).
+ ///
+ /// The username.
+ public virtual string Username { get; set; }
+
+ ///
+ /// Gets or sets the password (HTTP basic authentication).
+ ///
+ /// The password.
+ public virtual string Password { get; set; }
+
+ ///
+ /// Gets the API key with prefix.
+ ///
+ /// API key identifier (authentication scheme).
+ /// API key with prefix.
+ public string GetApiKeyWithPrefix(string apiKeyIdentifier)
+ {
+ string apiKeyValue;
+ ApiKey.TryGetValue(apiKeyIdentifier, out apiKeyValue);
+ string apiKeyPrefix;
+ if (ApiKeyPrefix.TryGetValue(apiKeyIdentifier, out apiKeyPrefix))
+ {
+ return apiKeyPrefix + " " + apiKeyValue;
+ }
+
+ return apiKeyValue;
+ }
+
+ ///
+ /// Gets or sets certificate collection to be sent with requests.
+ ///
+ /// X509 Certificate collection.
+ public X509CertificateCollection ClientCertificates { get; set; }
+
+ ///
+ /// Gets or sets the access token for OAuth2 authentication.
+ ///
+ /// This helper property simplifies code generation.
+ ///
+ /// The access token.
+ public virtual string AccessToken { get; set; }
+
+ ///
+ /// Gets or sets the temporary folder path to store the files downloaded from the server.
+ ///
+ /// Folder path.
+ public virtual string TempFolderPath
+ {
+ get { return _tempFolderPath; }
+
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ _tempFolderPath = Path.GetTempPath();
+ return;
+ }
+
+ // create the directory if it does not exist
+ if (!Directory.Exists(value))
+ {
+ Directory.CreateDirectory(value);
+ }
+
+ // check if the path contains directory separator at the end
+ if (value[value.Length - 1] == Path.DirectorySeparatorChar)
+ {
+ _tempFolderPath = value;
+ }
+ else
+ {
+ _tempFolderPath = value + Path.DirectorySeparatorChar;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the date time format used when serializing in the ApiClient
+ /// By default, it's set to ISO 8601 - "o", for others see:
+ /// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx
+ /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
+ /// No validation is done to ensure that the string you're providing is valid
+ ///
+ /// The DateTimeFormat string
+ public virtual string DateTimeFormat
+ {
+ get { return _dateTimeFormat; }
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ // Never allow a blank or null string, go back to the default
+ _dateTimeFormat = ISO8601_DATETIME_FORMAT;
+ return;
+ }
+
+ // Caution, no validation when you choose date time format other than ISO 8601
+ // Take a look at the above links
+ _dateTimeFormat = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name.
+ ///
+ /// Whatever you set here will be prepended to the value defined in AddApiKey.
+ ///
+ /// An example invocation here might be:
+ ///
+ /// ApiKeyPrefix["Authorization"] = "Bearer";
+ ///
+ /// … where ApiKey["Authorization"] would then be used to set the value of your bearer token.
+ ///
+ ///
+ /// OAuth2 workflows should set tokens via AccessToken.
+ ///
+ ///
+ /// The prefix of the API key.
+ public virtual IDictionary ApiKeyPrefix
+ {
+ get { return _apiKeyPrefix; }
+ set
+ {
+ if (value == null)
+ {
+ throw new InvalidOperationException("ApiKeyPrefix collection may not be null.");
+ }
+ _apiKeyPrefix = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the API key based on the authentication name.
+ ///
+ /// The API key.
+ public virtual IDictionary ApiKey
+ {
+ get { return _apiKey; }
+ set
+ {
+ if (value == null)
+ {
+ throw new InvalidOperationException("ApiKey collection may not be null.");
+ }
+ _apiKey = value;
+ }
+ }
+ {{#servers.0}}
+
+ ///
+ /// Gets or sets the servers.
+ ///
+ /// The servers.
+ public virtual IList> Servers
+ {
+ get { return _servers; }
+ set
+ {
+ if (value == null)
+ {
+ throw new InvalidOperationException("Servers may not be null.");
+ }
+ _servers = value;
+ }
+ }
+
+ ///
+ /// Returns URL based on server settings without providing values
+ /// for the variables
+ ///
+ /// Array index of the server settings.
+ /// The server URL.
+ public string GetServerUrl(int index)
+ {
+ return GetServerUrl(index, null);
+ }
+
+ ///
+ /// Returns URL based on server settings.
+ ///
+ /// Array index of the server settings.
+ /// Dictionary of the variables and the corresponding values.
+ /// The server URL.
+ public string GetServerUrl(int index, Dictionary inputVariables)
+ {
+ if (index < 0 || index >= Servers.Count)
+ {
+ throw new InvalidOperationException($"Invalid index {index} when selecting the server. Must be less than {Servers.Count}.");
+ }
+
+ if (inputVariables == null)
+ {
+ inputVariables = new Dictionary();
+ }
+
+ IReadOnlyDictionary server = Servers[index];
+ string url = (string)server["url"];
+
+ // go through variable and assign a value
+ foreach (KeyValuePair variable in (IReadOnlyDictionary)server["variables"])
+ {
+
+ IReadOnlyDictionary serverVariables = (IReadOnlyDictionary)(variable.Value);
+
+ if (inputVariables.ContainsKey(variable.Key))
+ {
+ if (((List)serverVariables["enum_values"]).Contains(inputVariables[variable.Key]))
+ {
+ url = url.Replace("{" + variable.Key + "}", inputVariables[variable.Key]);
+ }
+ else
+ {
+ throw new InvalidOperationException($"The variable `{variable.Key}` in the server URL has invalid value #{inputVariables[variable.Key]}. Must be {(List)serverVariables["enum_values"]}");
+ }
+ }
+ else
+ {
+ // use defualt value
+ url = url.Replace("{" + variable.Key + "}", (string)serverVariables["default_value"]);
+ }
+ }
+
+ return url;
+ }
+ {{/servers.0}}
+ {{#hasHttpSignatureMethods}}
+
+ ///
+ /// Gets and Sets the HttpSigningConfiuration
+ ///
+ public HttpSigningConfiguration HttpSigningConfiguration
+ {
+ get { return _HttpSigningConfiguration; }
+ set { _HttpSigningConfiguration = value; }
+ }
+ {{/hasHttpSignatureMethods}}
+
+ #endregion Properties
+
+ #region Methods
+
+ ///
+ /// Returns a string with essential information for debugging.
+ ///
+ public static string ToDebugReport()
+ {
+ string report = "C# SDK ({{{packageName}}}) Debug Report:\n";
+ report += " OS: " + System.Environment.OSVersion + "\n";
+ report += " .NET Framework Version: " + System.Environment.Version + "\n";
+ report += " Version of the API: {{{version}}}\n";
+ report += " SDK Package Version: {{{packageVersion}}}\n";
+
+ return report;
+ }
+
+ ///
+ /// Add Api Key Header.
+ ///
+ /// Api Key name.
+ /// Api Key value.
+ ///
+ public void AddApiKey(string key, string value)
+ {
+ ApiKey[key] = value;
+ }
+
+ ///
+ /// Sets the API key prefix.
+ ///
+ /// Api Key name.
+ /// Api Key value.
+ public void AddApiKeyPrefix(string key, string value)
+ {
+ ApiKeyPrefix[key] = value;
+ }
+
+ #endregion Methods
+
+ #region Static Members
+ ///
+ /// Merge configurations.
+ ///
+ /// First configuration.
+ /// Second configuration.
+ /// Merged configuration.
+ public static IReadableConfiguration MergeConfigurations(IReadableConfiguration first, IReadableConfiguration second)
+ {
+ if (second == null) return first ?? GlobalConfiguration.Instance;
+
+ Dictionary apiKey = first.ApiKey.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+ Dictionary apiKeyPrefix = first.ApiKeyPrefix.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+ Dictionary defaultHeaders = first.DefaultHeaders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+
+ foreach (var kvp in second.ApiKey) apiKey[kvp.Key] = kvp.Value;
+ foreach (var kvp in second.ApiKeyPrefix) apiKeyPrefix[kvp.Key] = kvp.Value;
+ foreach (var kvp in second.DefaultHeaders) defaultHeaders[kvp.Key] = kvp.Value;
+
+ var config = new Configuration
+ {
+ ApiKey = apiKey,
+ ApiKeyPrefix = apiKeyPrefix,
+ DefaultHeaders = defaultHeaders,
+ BasePath = second.BasePath ?? first.BasePath,
+ Timeout = second.Timeout,
+ Proxy = second.Proxy ?? first.Proxy,
+ UserAgent = second.UserAgent ?? first.UserAgent,
+ Username = second.Username ?? first.Username,
+ Password = second.Password ?? first.Password,
+ AccessToken = second.AccessToken ?? first.AccessToken,
+ {{#hasHttpSignatureMethods}}
+ HttpSigningConfiguration = second.HttpSigningConfiguration ?? first.HttpSigningConfiguration,
+ {{/hasHttpSignatureMethods}}
+ TempFolderPath = second.TempFolderPath ?? first.TempFolderPath,
+ DateTimeFormat = second.DateTimeFormat ?? first.DateTimeFormat
+ };
+ return config;
+ }
+ #endregion Static Members
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ExceptionFactory.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ExceptionFactory.mustache
new file mode 100644
index 00000000000..4a141f6f1ae
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ExceptionFactory.mustache
@@ -0,0 +1,14 @@
+{{>partial_header}}
+
+using System;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// A delegate to ExceptionFactory method
+ ///
+ /// Method name
+ /// Response
+ /// Exceptions
+ {{>visibility}} delegate Exception ExceptionFactory(string methodName, IApiResponse response);
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/GlobalConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/GlobalConfiguration.mustache
new file mode 100644
index 00000000000..bdfa4b99cc1
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/GlobalConfiguration.mustache
@@ -0,0 +1,59 @@
+{{>partial_header}}
+
+using System.Collections.Generic;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// provides a compile-time extension point for globally configuring
+ /// API Clients.
+ ///
+ ///
+ /// A customized implementation via partial class may reside in another file and may
+ /// be excluded from automatic generation via a .openapi-generator-ignore file.
+ ///
+ public partial class GlobalConfiguration : Configuration
+ {
+ #region Private Members
+
+ private static readonly object GlobalConfigSync = new { };
+ private static IReadableConfiguration _globalConfiguration;
+
+ #endregion Private Members
+
+ #region Constructors
+
+ ///
+ private GlobalConfiguration()
+ {
+ }
+
+ ///
+ public GlobalConfiguration(IDictionary defaultHeader, IDictionary apiKey, IDictionary apiKeyPrefix, string basePath = "http://localhost:3000/api") : base(defaultHeader, apiKey, apiKeyPrefix, basePath)
+ {
+ }
+
+ static GlobalConfiguration()
+ {
+ Instance = new GlobalConfiguration();
+ }
+
+ #endregion Constructors
+
+ ///
+ /// Gets or sets the default Configuration.
+ ///
+ /// Configuration.
+ public static IReadableConfiguration Instance
+ {
+ get { return _globalConfiguration; }
+ set
+ {
+ lock (GlobalConfigSync)
+ {
+ _globalConfiguration = value;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpMethod.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpMethod.mustache
new file mode 100644
index 00000000000..904a042a9ac
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpMethod.mustache
@@ -0,0 +1,25 @@
+{{>partial_header}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Http methods supported by swagger
+ ///
+ public enum HttpMethod
+ {
+ /// HTTP GET request.
+ Get,
+ /// HTTP POST request.
+ Post,
+ /// HTTP PUT request.
+ Put,
+ /// HTTP DELETE request.
+ Delete,
+ /// HTTP HEAD request.
+ Head,
+ /// HTTP OPTIONS request.
+ Options,
+ /// HTTP PATCH request.
+ Patch
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpSigningConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpSigningConfiguration.mustache
new file mode 100644
index 00000000000..cc8c275e40b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/HttpSigningConfiguration.mustache
@@ -0,0 +1,757 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Cryptography;
+using System.Text;
+using System.Web;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Class for HttpSigning auth related parameter and methods
+ ///
+ public class HttpSigningConfiguration
+ {
+ #region
+ ///
+ /// Initailize the HashAlgorithm and SigningAlgorithm to default value
+ ///
+ public HttpSigningConfiguration()
+ {
+ HashAlgorithm = HashAlgorithmName.SHA256;
+ SigningAlgorithm = "PKCS1-v15";
+ }
+ #endregion
+
+ #region Properties
+ ///
+ ///Gets the Api keyId
+ ///
+ public string KeyId { get; set; }
+
+ ///
+ /// Gets the Key file path
+ ///
+ public string KeyFilePath { get; set; }
+
+ ///
+ /// Gets the key pass phrase for password protected key
+ ///
+ public SecureString KeyPassPhrase { get; set; }
+
+ ///
+ /// Gets the HTTP signing header
+ ///
+ public List HttpSigningHeader { get; set; }
+
+ ///
+ /// Gets the hash algorithm sha256 or sha512
+ ///
+ public HashAlgorithmName HashAlgorithm { get; set; }
+
+ ///
+ /// Gets the signing algorithm
+ ///
+ public string SigningAlgorithm { get; set; }
+
+ ///
+ /// Gets the Signature validaty period in seconds
+ ///
+ public int SignatureValidityPeriod { get; set; }
+
+ #endregion
+
+ #region enum
+ private enum PrivateKeyType
+ {
+ None = 0,
+ RSA = 1,
+ ECDSA = 2,
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Gets the Headers for HttpSigning
+ ///
+ /// Base path
+ /// HTTP method
+ /// Path
+ /// Request options
+ ///
+ internal Dictionary GetHttpSignedHeader(string basePath,string method, string path, RequestOptions requestOptions)
+ {
+ const string HEADER_REQUEST_TARGET = "(request-target)";
+ //The time when the HTTP signature expires. The API server should reject HTTP requests
+ //that have expired.
+ const string HEADER_EXPIRES = "(expires)";
+ //The 'Date' header.
+ const string HEADER_DATE = "Date";
+ //The 'Host' header.
+ const string HEADER_HOST = "Host";
+ //The time when the HTTP signature was generated.
+ const string HEADER_CREATED = "(created)";
+ //When the 'Digest' header is included in the HTTP signature, the client automatically
+ //computes the digest of the HTTP request body, per RFC 3230.
+ const string HEADER_DIGEST = "Digest";
+ //The 'Authorization' header is automatically generated by the client. It includes
+ //the list of signed headers and a base64-encoded signature.
+ const string HEADER_AUTHORIZATION = "Authorization";
+
+ //Hash table to store singed headers
+ var HttpSignedRequestHeader = new Dictionary();
+ var HttpSignatureHeader = new Dictionary();
+
+ if (HttpSigningHeader.Count == 0)
+ {
+ HttpSigningHeader.Add("(created)");
+ }
+
+ if (requestOptions.PathParameters != null)
+ {
+ foreach (var pathParam in requestOptions.PathParameters)
+ {
+ var tempPath = path.Replace(pathParam.Key, "0");
+ path = string.Format(tempPath, pathParam.Value);
+ }
+ }
+
+ var httpValues = HttpUtility.ParseQueryString(string.Empty);
+ foreach (var parameter in requestOptions.QueryParameters)
+ {
+#if (NETCOREAPP)
+ if (parameter.Value.Count > 1)
+ { // array
+ foreach (var value in parameter.Value)
+ {
+ httpValues.Add(HttpUtility.UrlEncode(parameter.Key) + "[]", value);
+ }
+ }
+ else
+ {
+ httpValues.Add(HttpUtility.UrlEncode(parameter.Key), parameter.Value[0]);
+ }
+#else
+ if (parameter.Value.Count > 1)
+ { // array
+ foreach (var value in parameter.Value)
+ {
+ httpValues.Add(parameter.Key + "[]", value);
+ }
+ }
+ else
+ {
+ httpValues.Add(parameter.Key, parameter.Value[0]);
+ }
+#endif
+ }
+ var uriBuilder = new UriBuilder(string.Concat(basePath, path));
+ uriBuilder.Query = httpValues.ToString().Replace("+", "%20");
+
+ var dateTime = DateTime.Now;
+ string Digest = string.Empty;
+
+ //get the body
+ string requestBody = string.Empty;
+ if (requestOptions.Data != null)
+ {
+ var serializerSettings = new JsonSerializerSettings();
+ requestBody = JsonConvert.SerializeObject(requestOptions.Data, serializerSettings);
+ }
+
+ if (HashAlgorithm == HashAlgorithmName.SHA256)
+ {
+ var bodyDigest = GetStringHash(HashAlgorithm.ToString(), requestBody);
+ Digest = string.Format("SHA-256={0}", Convert.ToBase64String(bodyDigest));
+ }
+ else if (HashAlgorithm == HashAlgorithmName.SHA512)
+ {
+ var bodyDigest = GetStringHash(HashAlgorithm.ToString(), requestBody);
+ Digest = string.Format("SHA-512={0}", Convert.ToBase64String(bodyDigest));
+ }
+ else
+ {
+ throw new Exception(string.Format("{0} not supported", HashAlgorithm));
+ }
+
+
+ foreach (var header in HttpSigningHeader)
+ {
+ if (header.Equals(HEADER_REQUEST_TARGET))
+ {
+ var targetUrl = string.Format("{0} {1}{2}", method.ToLower(), uriBuilder.Path, uriBuilder.Query);
+ HttpSignatureHeader.Add(header.ToLower(), targetUrl);
+ }
+ else if (header.Equals(HEADER_EXPIRES))
+ {
+ var expireDateTime = dateTime.AddSeconds(SignatureValidityPeriod);
+ HttpSignatureHeader.Add(header.ToLower(), GetUnixTime(expireDateTime).ToString());
+ }
+ else if (header.Equals(HEADER_DATE))
+ {
+ var utcDateTime = dateTime.ToString("r").ToString();
+ HttpSignatureHeader.Add(header.ToLower(), utcDateTime);
+ HttpSignedRequestHeader.Add(HEADER_DATE, utcDateTime);
+ }
+ else if (header.Equals(HEADER_HOST))
+ {
+ HttpSignatureHeader.Add(header.ToLower(), uriBuilder.Host);
+ HttpSignedRequestHeader.Add(HEADER_HOST, uriBuilder.Host);
+ }
+ else if (header.Equals(HEADER_CREATED))
+ {
+ HttpSignatureHeader.Add(header.ToLower(), GetUnixTime(dateTime).ToString());
+ }
+ else if (header.Equals(HEADER_DIGEST))
+ {
+ HttpSignedRequestHeader.Add(HEADER_DIGEST, Digest);
+ HttpSignatureHeader.Add(header.ToLower(), Digest);
+ }
+ else
+ {
+ bool isHeaderFound = false;
+ foreach (var item in requestOptions.HeaderParameters)
+ {
+ if (string.Equals(item.Key, header, StringComparison.OrdinalIgnoreCase))
+ {
+ HttpSignatureHeader.Add(header.ToLower(), item.Value.ToString());
+ isHeaderFound = true;
+ break;
+ }
+ }
+ if (!isHeaderFound)
+ {
+ throw new Exception(string.Format("Cannot sign HTTP request.Request does not contain the {0} header.",header));
+ }
+ }
+
+ }
+ var headersKeysString = string.Join(" ", HttpSignatureHeader.Keys);
+ var headerValuesList = new List();
+
+ foreach (var keyVal in HttpSignatureHeader)
+ {
+ headerValuesList.Add(string.Format("{0}: {1}", keyVal.Key, keyVal.Value));
+
+ }
+ //Concatinate headers value separated by new line
+ var headerValuesString = string.Join("\n", headerValuesList);
+ var signatureStringHash = GetStringHash(HashAlgorithm.ToString(), headerValuesString);
+ string headerSignatureStr = null;
+ var keyType = GetKeyType(KeyFilePath);
+
+ if (keyType == PrivateKeyType.RSA)
+ {
+ headerSignatureStr = GetRSASignature(signatureStringHash);
+ }
+ else if (keyType == PrivateKeyType.ECDSA)
+ {
+ headerSignatureStr = GetECDSASignature(signatureStringHash);
+ }
+ var cryptographicScheme = "hs2019";
+ var authorizationHeaderValue = string.Format("Signature keyId=\"{0}\",algorithm=\"{1}\"",
+ KeyId, cryptographicScheme);
+
+ if (HttpSignatureHeader.ContainsKey(HEADER_CREATED))
+ {
+ authorizationHeaderValue += string.Format(",created={0}", HttpSignatureHeader[HEADER_CREATED]);
+ }
+
+ if (HttpSignatureHeader.ContainsKey(HEADER_EXPIRES))
+ {
+ authorizationHeaderValue += string.Format(",expires={0}", HttpSignatureHeader[HEADER_EXPIRES]);
+ }
+
+ authorizationHeaderValue += string.Format(",headers=\"{0}\",signature=\"{1}\"",
+ headersKeysString, headerSignatureStr);
+
+ HttpSignedRequestHeader.Add(HEADER_AUTHORIZATION, authorizationHeaderValue);
+
+ return HttpSignedRequestHeader;
+ }
+
+ private byte[] GetStringHash(string hashName, string stringToBeHashed)
+ {
+ var hashAlgorithm = System.Security.Cryptography.HashAlgorithm.Create(hashName);
+ var bytes = Encoding.UTF8.GetBytes(stringToBeHashed);
+ var stringHash = hashAlgorithm.ComputeHash(bytes);
+ return stringHash;
+ }
+
+ private int GetUnixTime(DateTime date2)
+ {
+ DateTime date1 = new DateTime(1970, 01, 01);
+ TimeSpan timeSpan = date2 - date1;
+ return (int)timeSpan.TotalSeconds;
+ }
+
+ private string GetRSASignature(byte[] stringToSign)
+ {
+ RSA rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase);
+ if (SigningAlgorithm == "RSASSA-PSS")
+ {
+ var signedbytes = rsa.SignHash(stringToSign, HashAlgorithm, RSASignaturePadding.Pss);
+ return Convert.ToBase64String(signedbytes);
+ }
+ else if (SigningAlgorithm == "PKCS1-v15")
+ {
+ var signedbytes = rsa.SignHash(stringToSign, HashAlgorithm, RSASignaturePadding.Pkcs1);
+ return Convert.ToBase64String(signedbytes);
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// Gets the ECDSA signature
+ ///
+ ///
+ ///
+ private string GetECDSASignature(byte[] dataToSign)
+ {
+ if (!File.Exists(KeyFilePath))
+ {
+ throw new Exception("key file path does not exist.");
+ }
+
+ var ecKeyHeader = "-----BEGIN EC PRIVATE KEY-----";
+ var ecKeyFooter = "-----END EC PRIVATE KEY-----";
+ var keyStr = File.ReadAllText(KeyFilePath);
+ var ecKeyBase64String = keyStr.Replace(ecKeyHeader, "").Replace(ecKeyFooter, "").Trim();
+ var keyBytes = System.Convert.FromBase64String(ecKeyBase64String);
+ var ecdsa = ECDsa.Create();
+
+#if (NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0)
+ var byteCount = 0;
+ if (KeyPassPhrase != null)
+ {
+ IntPtr unmanagedString = IntPtr.Zero;
+ try
+ {
+ // convert secure string to byte array
+ unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(KeyPassPhrase);
+ ecdsa.ImportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(Marshal.PtrToStringUni(unmanagedString)), keyBytes, out byteCount);
+ }
+ finally
+ {
+ if (unmanagedString != IntPtr.Zero)
+ {
+ Marshal.ZeroFreeBSTR(unmanagedString);
+ }
+ }
+ }
+ else
+ {
+ ecdsa.ImportPkcs8PrivateKey(keyBytes, out byteCount);
+ }
+ var signedBytes = ecdsa.SignHash(dataToSign);
+ var derBytes = ConvertToECDSAANS1Format(signedBytes);
+ var signedString = System.Convert.ToBase64String(derBytes);
+
+ return signedString;
+#else
+ throw new Exception("ECDSA signing is supported only on NETCOREAPP3_0 and above");
+#endif
+
+ }
+
+ private byte[] ConvertToECDSAANS1Format(byte[] signedBytes)
+ {
+ var derBytes = new List();
+ byte derLength = 68; //default lenght for ECDSA code signinged bit 0x44
+ byte rbytesLength = 32; //R length 0x20
+ byte sbytesLength = 32; //S length 0x20
+ var rBytes = new List();
+ var sBytes = new List();
+ for (int i = 0; i < 32; i++)
+ {
+ rBytes.Add(signedBytes[i]);
+ }
+ for (int i = 32; i < 64; i++)
+ {
+ sBytes.Add(signedBytes[i]);
+ }
+
+ if (rBytes[0] > 0x7F)
+ {
+ derLength++;
+ rbytesLength++;
+ var tempBytes = new List();
+ tempBytes.AddRange(rBytes);
+ rBytes.Clear();
+ rBytes.Add(0x00);
+ rBytes.AddRange(tempBytes);
+ }
+
+ if (sBytes[0] > 0x7F)
+ {
+ derLength++;
+ sbytesLength++;
+ var tempBytes = new List();
+ tempBytes.AddRange(sBytes);
+ sBytes.Clear();
+ sBytes.Add(0x00);
+ sBytes.AddRange(tempBytes);
+
+ }
+
+ derBytes.Add(48); //start of the sequence 0x30
+ derBytes.Add(derLength); //total length r lenth, type and r bytes
+
+ derBytes.Add(2); //tag for integer
+ derBytes.Add(rbytesLength); //length of r
+ derBytes.AddRange(rBytes);
+
+ derBytes.Add(2); //tag for integer
+ derBytes.Add(sbytesLength); //length of s
+ derBytes.AddRange(sBytes);
+ return derBytes.ToArray();
+ }
+
+ private RSACryptoServiceProvider GetRSAProviderFromPemFile(string pemfile, SecureString keyPassPharse = null)
+ {
+ const string pempubheader = "-----BEGIN PUBLIC KEY-----";
+ const string pempubfooter = "-----END PUBLIC KEY-----";
+ bool isPrivateKeyFile = true;
+ byte[] pemkey = null;
+
+ if (!File.Exists(pemfile))
+ {
+ throw new Exception("private key file does not exist.");
+ }
+ string pemstr = File.ReadAllText(pemfile).Trim();
+
+ if (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter))
+ {
+ isPrivateKeyFile = false;
+ }
+
+ if (isPrivateKeyFile)
+ {
+ pemkey = ConvertPrivateKeyToBytes(pemstr, keyPassPharse);
+ if (pemkey == null)
+ {
+ return null;
+ }
+ return DecodeRSAPrivateKey(pemkey);
+ }
+ return null;
+ }
+
+ private byte[] ConvertPrivateKeyToBytes(string instr, SecureString keyPassPharse = null)
+ {
+ const string pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
+ const string pemprivfooter = "-----END RSA PRIVATE KEY-----";
+ string pemstr = instr.Trim();
+ byte[] binkey;
+
+ if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter))
+ {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder(pemstr);
+ sb.Replace(pemprivheader, "");
+ sb.Replace(pemprivfooter, "");
+ string pvkstr = sb.ToString().Trim();
+
+ try
+ { // if there are no PEM encryption info lines, this is an UNencrypted PEM private key
+ binkey = Convert.FromBase64String(pvkstr);
+ return binkey;
+ }
+ catch (System.FormatException)
+ {
+ StringReader str = new StringReader(pvkstr);
+
+ //-------- read PEM encryption info. lines and extract salt -----
+ if (!str.ReadLine().StartsWith("Proc-Type: 4,ENCRYPTED"))
+ {
+ return null;
+ }
+ string saltline = str.ReadLine();
+ if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,"))
+ {
+ return null;
+ }
+ string saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim();
+ byte[] salt = new byte[saltstr.Length / 2];
+ for (int i = 0; i < salt.Length; i++)
+ salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16);
+ if (!(str.ReadLine() == ""))
+ {
+ return null;
+ }
+
+ //------ remaining b64 data is encrypted RSA key ----
+ string encryptedstr = str.ReadToEnd();
+
+ try
+ { //should have b64 encrypted RSA key now
+ binkey = Convert.FromBase64String(encryptedstr);
+ }
+ catch (System.FormatException)
+ { //data is not in base64 fromat
+ return null;
+ }
+
+ byte[] deskey = GetEncryptedKey(salt, keyPassPharse, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
+ if (deskey == null)
+ {
+ return null;
+ }
+
+ //------ Decrypt the encrypted 3des-encrypted RSA private key ------
+ byte[] rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
+ return rsakey;
+ }
+ }
+
+ private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
+ {
+ byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
+
+ // --------- Set up stream to decode the asn.1 encoded RSA private key ------
+ MemoryStream mem = new MemoryStream(privkey);
+ BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
+ byte bt = 0;
+ ushort twobytes = 0;
+ int elems = 0;
+ try
+ {
+ twobytes = binr.ReadUInt16();
+ if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+ {
+ binr.ReadByte(); //advance 1 byte
+ }
+ else if (twobytes == 0x8230)
+ {
+ binr.ReadInt16(); //advance 2 bytes
+ }
+ else
+ {
+ return null;
+ }
+
+ twobytes = binr.ReadUInt16();
+ if (twobytes != 0x0102) //version number
+ {
+ return null;
+ }
+ bt = binr.ReadByte();
+ if (bt != 0x00)
+ {
+ return null;
+ }
+
+ //------ all private key components are Integer sequences ----
+ elems = GetIntegerSize(binr);
+ MODULUS = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ E = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ D = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ P = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ Q = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ DP = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ DQ = binr.ReadBytes(elems);
+
+ elems = GetIntegerSize(binr);
+ IQ = binr.ReadBytes(elems);
+
+ // ------- create RSACryptoServiceProvider instance and initialize with public key -----
+ RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
+ RSAParameters RSAparams = new RSAParameters();
+ RSAparams.Modulus = MODULUS;
+ RSAparams.Exponent = E;
+ RSAparams.D = D;
+ RSAparams.P = P;
+ RSAparams.Q = Q;
+ RSAparams.DP = DP;
+ RSAparams.DQ = DQ;
+ RSAparams.InverseQ = IQ;
+ RSA.ImportParameters(RSAparams);
+ return RSA;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ finally
+ {
+ binr.Close();
+ }
+ }
+
+ private int GetIntegerSize(BinaryReader binr)
+ {
+ byte bt = 0;
+ byte lowbyte = 0x00;
+ byte highbyte = 0x00;
+ int count = 0;
+ bt = binr.ReadByte();
+ if (bt != 0x02) //expect integer
+ {
+ return 0;
+ }
+ bt = binr.ReadByte();
+
+ if (bt == 0x81)
+ {
+ count = binr.ReadByte(); // data size in next byte
+ }
+ else if (bt == 0x82)
+ {
+ highbyte = binr.ReadByte(); // data size in next 2 bytes
+ lowbyte = binr.ReadByte();
+ byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
+ count = BitConverter.ToInt32(modint, 0);
+ }
+ else
+ {
+ count = bt; // we already have the data size
+ }
+ while (binr.ReadByte() == 0x00)
+ {
+ //remove high order zeros in data
+ count -= 1;
+ }
+ binr.BaseStream.Seek(-1, SeekOrigin.Current);
+ //last ReadByte wasn't a removed zero, so back up a byte
+ return count;
+ }
+
+ private byte[] GetEncryptedKey(byte[] salt, SecureString secpswd, int count, int miter)
+ {
+ IntPtr unmanagedPswd = IntPtr.Zero;
+ int HASHLENGTH = 16; //MD5 bytes
+ byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store contatenated Mi hashed results
+
+ byte[] psbytes = new byte[secpswd.Length];
+ unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
+ Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
+ Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
+
+ // --- contatenate salt and pswd bytes into fixed data array ---
+ byte[] data00 = new byte[psbytes.Length + salt.Length];
+ Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes
+ Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes
+
+ // ---- do multi-hashing and contatenate results D1, D2 ... into keymaterial bytes ----
+ MD5 md5 = new MD5CryptoServiceProvider();
+ byte[] result = null;
+ byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget
+
+ for (int j = 0; j < miter; j++)
+ {
+ // ---- Now hash consecutively for count times ------
+ if (j == 0)
+ {
+ result = data00; //initialize
+ }
+ else
+ {
+ Array.Copy(result, hashtarget, result.Length);
+ Array.Copy(data00, 0, hashtarget, result.Length, data00.Length);
+ result = hashtarget;
+ }
+
+ for (int i = 0; i < count; i++)
+ result = md5.ComputeHash(result);
+ Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //contatenate to keymaterial
+ }
+ byte[] deskey = new byte[24];
+ Array.Copy(keymaterial, deskey, deskey.Length);
+
+ Array.Clear(psbytes, 0, psbytes.Length);
+ Array.Clear(data00, 0, data00.Length);
+ Array.Clear(result, 0, result.Length);
+ Array.Clear(hashtarget, 0, hashtarget.Length);
+ Array.Clear(keymaterial, 0, keymaterial.Length);
+ return deskey;
+ }
+
+ private byte[] DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV)
+ {
+ MemoryStream memst = new MemoryStream();
+ TripleDES alg = TripleDES.Create();
+ alg.Key = desKey;
+ alg.IV = IV;
+ try
+ {
+ CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write);
+ cs.Write(cipherData, 0, cipherData.Length);
+ cs.Close();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ byte[] decryptedData = memst.ToArray();
+ return decryptedData;
+ }
+
+ ///
+ /// Detect the key type from the pem file.
+ ///
+ /// key file path in pem format
+ ///
+ private PrivateKeyType GetKeyType(string keyFilePath)
+ {
+ if (!File.Exists(keyFilePath))
+ {
+ throw new Exception("Key file path does not exist.");
+ }
+
+ var ecPrivateKeyHeader = "BEGIN EC PRIVATE KEY";
+ var ecPrivateKeyFooter = "END EC PRIVATE KEY";
+ var rsaPrivateKeyHeader = "BEGIN RSA PRIVATE KEY";
+ var rsaPrivateFooter = "END RSA PRIVATE KEY";
+ //var pkcs8Header = "BEGIN PRIVATE KEY";
+ //var pkcs8Footer = "END PRIVATE KEY";
+ var keyType = PrivateKeyType.None;
+ var key = File.ReadAllLines(keyFilePath);
+
+ if (key[0].ToString().Contains(rsaPrivateKeyHeader) &&
+ key[key.Length - 1].ToString().Contains(rsaPrivateFooter))
+ {
+ keyType = PrivateKeyType.RSA;
+ }
+ else if (key[0].ToString().Contains(ecPrivateKeyHeader) &&
+ key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter))
+ {
+ keyType = PrivateKeyType.ECDSA;
+ }
+ else if (key[0].ToString().Contains(ecPrivateKeyHeader) &&
+ key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter))
+ {
+
+ /* this type of key can hold many type different types of private key, but here due lack of pem header
+ Considering this as EC key
+ */
+ //TODO :- update the key based on oid
+ keyType = PrivateKeyType.ECDSA;
+ }
+ else
+ {
+ throw new Exception("Either the key is invalid or key is not supported");
+
+ }
+ return keyType;
+ }
+ #endregion
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IApiAccessor.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IApiAccessor.mustache
new file mode 100644
index 00000000000..a269f56e904
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IApiAccessor.mustache
@@ -0,0 +1,29 @@
+{{>partial_header}}
+
+using System;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Represents configuration aspects required to interact with the API endpoints.
+ ///
+ {{>visibility}} interface IApiAccessor
+ {
+ ///
+ /// Gets or sets the configuration object
+ ///
+ /// An instance of the Configuration
+ IReadableConfiguration Configuration { get; set; }
+
+ ///
+ /// Gets the base path of the API client.
+ ///
+ /// The base path
+ string GetBasePath();
+
+ ///
+ /// Provides a factory method hook for the creation of exceptions.
+ ///
+ ExceptionFactory ExceptionFactory { get; set; }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IAsynchronousClient.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IAsynchronousClient.mustache
new file mode 100644
index 00000000000..f0c88fae473
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IAsynchronousClient.mustache
@@ -0,0 +1,92 @@
+{{>partial_header}}
+
+using System;
+using System.Threading.Tasks;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Contract for Asynchronous RESTful API interactions.
+ ///
+ /// This interface allows consumers to provide a custom API accessor client.
+ ///
+ public interface IAsynchronousClient
+ {
+ ///
+ /// Executes a non-blocking call to some using the GET http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> GetAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the POST http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> PostAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the PUT http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> PutAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the DELETE http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> DeleteAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the HEAD http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> HeadAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the OPTIONS http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> OptionsAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// Executes a non-blocking call to some using the PATCH http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// Cancellation Token to cancel the request.
+ /// The return type.
+ /// A task eventually representing the response data, decorated with
+ Task> PatchAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IReadableConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IReadableConfiguration.mustache
new file mode 100644
index 00000000000..7c6487d92f8
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/IReadableConfiguration.mustache
@@ -0,0 +1,114 @@
+{{>partial_header}}
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Represents a readable-only configuration contract.
+ ///
+ public interface IReadableConfiguration
+ {
+ ///
+ /// Gets the access token.
+ ///
+ /// Access token.
+ string AccessToken { get; }
+
+ ///
+ /// Gets the API key.
+ ///
+ /// API key.
+ IDictionary ApiKey { get; }
+
+ ///
+ /// Gets the API key prefix.
+ ///
+ /// API key prefix.
+ IDictionary ApiKeyPrefix { get; }
+
+ ///
+ /// Gets the base path.
+ ///
+ /// Base path.
+ string BasePath { get; }
+
+ ///
+ /// Gets the date time format.
+ ///
+ /// Date time foramt.
+ string DateTimeFormat { get; }
+
+ ///
+ /// Gets the default header.
+ ///
+ /// Default header.
+ [Obsolete("Use DefaultHeaders instead.")]
+ IDictionary DefaultHeader { get; }
+
+ ///
+ /// Gets the default headers.
+ ///
+ /// Default headers.
+ IDictionary DefaultHeaders { get; }
+
+ ///
+ /// Gets the temp folder path.
+ ///
+ /// Temp folder path.
+ string TempFolderPath { get; }
+
+ ///
+ /// Gets the HTTP connection timeout (in milliseconds)
+ ///
+ /// HTTP connection timeout.
+ int Timeout { get; }
+
+ ///
+ /// Gets the proxy.
+ ///
+ /// Proxy.
+ WebProxy Proxy { get; }
+
+ ///
+ /// Gets the user agent.
+ ///
+ /// User agent.
+ string UserAgent { get; }
+
+ ///
+ /// Gets the username.
+ ///
+ /// Username.
+ string Username { get; }
+
+ ///
+ /// Gets the password.
+ ///
+ /// Password.
+ string Password { get; }
+
+ ///
+ /// Gets the API key with prefix.
+ ///
+ /// API key identifier (authentication scheme).
+ /// API key with prefix.
+ string GetApiKeyWithPrefix(string apiKeyIdentifier);
+
+ ///
+ /// Gets certificate collection to be sent with requests.
+ ///
+ /// X509 Certificate collection.
+ X509CertificateCollection ClientCertificates { get; }
+ {{#hasHttpSignatureMethods}}
+
+ ///
+ /// Gets the HttpSigning configuration
+ ///
+ HttpSigningConfiguration HttpSigningConfiguration { get; }
+ {{/hasHttpSignatureMethods}}
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ISynchronousClient.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ISynchronousClient.mustache
new file mode 100644
index 00000000000..c09bfbfed66
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ISynchronousClient.mustache
@@ -0,0 +1,85 @@
+{{>partial_header}}
+
+using System;
+using System.IO;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Contract for Synchronous RESTful API interactions.
+ ///
+ /// This interface allows consumers to provide a custom API accessor client.
+ ///
+ public interface ISynchronousClient
+ {
+ ///
+ /// Executes a blocking call to some using the GET http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Get(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the POST http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Post(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the PUT http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Put(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the DELETE http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Delete(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the HEAD http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Head(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the OPTIONS http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Options(string path, RequestOptions options, IReadableConfiguration configuration = null);
+
+ ///
+ /// Executes a blocking call to some using the PATCH http verb.
+ ///
+ /// The relative path to invoke.
+ /// The request parameters to pass along to the client.
+ /// Per-request configurable settings.
+ /// The return type.
+ /// The response data, decorated with
+ ApiResponse Patch(string path, RequestOptions options, IReadableConfiguration configuration = null);
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/JsonSubTypesTests.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/JsonSubTypesTests.mustache
new file mode 100644
index 00000000000..9b1f66624d7
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/JsonSubTypesTests.mustache
@@ -0,0 +1,125 @@
+{{>partial_header}}
+
+using System.Collections.Generic;
+using System.Linq;
+using JsonSubTypes;
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+using {{packageName}}.{{apiPackage}};
+using {{packageName}}.{{modelPackage}};
+using {{packageName}}.Client;
+
+namespace {{packageName}}.Test.Client
+{
+ public class JsonSubTypesTests
+ {
+ [Test]
+ public void TestSimpleJsonSubTypesExample()
+ {
+ var annimal =
+ JsonConvert.DeserializeObject("{\"Kind\":\"Dog\",\"Breed\":\"Jack Russell Terrier\"}");
+ Assert.AreEqual("Jack Russell Terrier", (annimal as Dog)?.Breed);
+ }
+
+ [Test]
+ public void DeserializeObjectWithCustomMapping()
+ {
+ var annimal =
+ JsonConvert.DeserializeObject("{\"Sound\":\"Bark\",\"Breed\":\"Jack Russell Terrier\"}");
+ Assert.AreEqual("Jack Russell Terrier", (annimal as Dog2)?.Breed);
+ }
+
+ [Test]
+ public void DeserializeObjectMappingByPropertyPresence()
+ {
+ string json =
+ "[{\"Department\":\"Department1\",\"JobTitle\":\"JobTitle1\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}," +
+ "{\"Department\":\"Department1\",\"JobTitle\":\"JobTitle1\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}," +
+ "{\"Skill\":\"Painter\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}]";
+
+
+ var persons = JsonConvert.DeserializeObject>(json);
+ Assert.AreEqual("Painter", (persons.Last() as Artist)?.Skill);
+ }
+ }
+
+ [JsonConverter(typeof(JsonSubtypes), "Kind")]
+ public interface IAnimal
+ {
+ string Kind { get; }
+ }
+
+ public class Dog : IAnimal
+ {
+ public Dog()
+ {
+ Kind = "Dog";
+ }
+
+ public string Kind { get; }
+ public string Breed { get; set; }
+ }
+
+ class Cat : IAnimal
+ {
+ public Cat()
+ {
+ Kind = "Cat";
+ }
+
+ public string Kind { get; }
+ bool Declawed { get; set; }
+ }
+
+ [JsonConverter(typeof(JsonSubtypes), "Sound")]
+ [JsonSubtypes.KnownSubType(typeof(Dog2), "Bark")]
+ [JsonSubtypes.KnownSubType(typeof(Cat2), "Meow")]
+ public class Animal2
+ {
+ public virtual string Sound { get; }
+ public string Color { get; set; }
+ }
+
+ public class Dog2 : Animal2
+ {
+ public Dog2()
+ {
+ Sound = "Bark";
+ }
+
+ public override string Sound { get; }
+ public string Breed { get; set; }
+ }
+
+ public class Cat2 : Animal2
+ {
+ public Cat2()
+ {
+ Sound = "Meow";
+ }
+
+ public override string Sound { get; }
+ public bool Declawed { get; set; }
+ }
+
+ [JsonConverter(typeof(JsonSubtypes))]
+ [JsonSubtypes.KnownSubTypeWithProperty(typeof(Employee), "JobTitle")]
+ [JsonSubtypes.KnownSubTypeWithProperty(typeof(Artist), "Skill")]
+ public class Person
+ {
+ public string FirstName { get; set; }
+ public string LastName { get; set; }
+ }
+
+ public class Employee : Person
+ {
+ public string Department { get; set; }
+ public string JobTitle { get; set; }
+ }
+
+ public class Artist : Person
+ {
+ public string Skill { get; set; }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Multimap.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Multimap.mustache
new file mode 100644
index 00000000000..8624af00924
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Multimap.mustache
@@ -0,0 +1,287 @@
+{{>partial_header}}
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// A dictionary in which one key has many associated values.
+ ///
+ /// The type of the key
+ /// The type of the value associated with the key.
+ public class Multimap : IDictionary>
+ {
+ #region Private Fields
+
+ private readonly Dictionary> _dictionary;
+
+ #endregion Private Fields
+
+ #region Constructors
+
+ ///
+ /// Empty Constructor.
+ ///
+ public Multimap()
+ {
+ _dictionary = new Dictionary>();
+ }
+
+ ///
+ /// Constructor with comparer.
+ ///
+ ///
+ public Multimap(IEqualityComparer comparer)
+ {
+ _dictionary = new Dictionary>(comparer);
+ }
+
+ #endregion Constructors
+
+ #region Enumerators
+
+ ///
+ /// To get the enumerator.
+ ///
+ /// Enumerator
+ public IEnumerator>> GetEnumerator()
+ {
+ return _dictionary.GetEnumerator();
+ }
+
+ ///
+ /// To get the enumerator.
+ ///
+ /// Enumerator
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _dictionary.GetEnumerator();
+ }
+
+ #endregion Enumerators
+
+ #region Public Members
+ ///
+ /// Add values to Multimap
+ ///
+ /// Key value pair
+ public void Add(KeyValuePair> item)
+ {
+ if (!TryAdd(item.Key, item.Value))
+ throw new InvalidOperationException("Could not add values to Multimap.");
+ }
+
+ ///
+ /// Add Multimap to Multimap
+ ///
+ /// Multimap
+ public void Add(Multimap multimap)
+ {
+ foreach (var item in multimap)
+ {
+ if (!TryAdd(item.Key, item.Value))
+ throw new InvalidOperationException("Could not add values to Multimap.");
+ }
+ }
+
+ ///
+ /// Clear Multimap
+ ///
+ public void Clear()
+ {
+ _dictionary.Clear();
+ }
+
+ ///
+ /// Determines whether Multimap contains the specified item.
+ ///
+ /// Key value pair
+ /// Method needs to be implemented
+ /// true if the Multimap contains the item; otherwise, false.
+ public bool Contains(KeyValuePair> item)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Copy items of the Multimap to an array,
+ /// starting at a particular array index.
+ ///
+ /// The array that is the destination of the items copied
+ /// from Multimap. The array must have zero-based indexing.
+ /// The zero-based index in array at which copying begins.
+ /// Method needs to be implemented
+ public void CopyTo(KeyValuePair>[] array, int arrayIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Removes the specified item from the Multimap.
+ ///
+ /// Key value pair
+ /// true if the item is successfully removed; otherwise, false.
+ /// Method needs to be implemented
+ public bool Remove(KeyValuePair> item)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Gets the number of items contained in the Multimap.
+ ///
+ public int Count => _dictionary.Count;
+
+ ///
+ /// Gets a value indicating whether the Multimap is read-only.
+ ///
+ public bool IsReadOnly => false;
+
+ ///
+ /// Adds an item with the provided key and value to the Multimap.
+ ///
+ /// The object to use as the key of the item to add.
+ /// The object to use as the value of the item to add.
+ /// Thrown when couldn't add the value to Multimap.
+ public void Add(TKey key, IList value)
+ {
+ if (value != null && value.Count > 0)
+ {
+ if (_dictionary.TryGetValue(key, out var list))
+ {
+ foreach (var k in value) list.Add(k);
+ }
+ else
+ {
+ list = new List(value);
+ if (!TryAdd(key, list))
+ throw new InvalidOperationException("Could not add values to Multimap.");
+ }
+ }
+ }
+
+ ///
+ /// Determines whether the Multimap contains an item with the specified key.
+ ///
+ /// The key to locate in the Multimap.
+ /// true if the Multimap contains an item with
+ /// the key; otherwise, false.
+ public bool ContainsKey(TKey key)
+ {
+ return _dictionary.ContainsKey(key);
+ }
+
+ ///
+ /// Removes item with the specified key from the Multimap.
+ ///
+ /// The key to locate in the Multimap.
+ /// true if the item is successfully removed; otherwise, false.
+ public bool Remove(TKey key)
+ {
+ return TryRemove(key, out var _);
+ }
+
+ ///
+ /// Gets the value associated with the specified key.
+ ///
+ /// The key whose value to get.
+ /// When this method returns, the value associated with the specified key, if the
+ /// key is found; otherwise, the default value for the type of the value parameter.
+ /// This parameter is passed uninitialized.
+ /// true if the object that implements Multimap contains
+ /// an item with the specified key; otherwise, false.
+ public bool TryGetValue(TKey key, out IList value)
+ {
+ return _dictionary.TryGetValue(key, out value);
+ }
+
+ ///
+ /// Gets or sets the item with the specified key.
+ ///
+ /// The key of the item to get or set.
+ /// The value of the specified key.
+ public IList this[TKey key]
+ {
+ get => _dictionary[key];
+ set => _dictionary[key] = value;
+ }
+
+ ///
+ /// Gets a System.Collections.Generic.ICollection containing the keys of the Multimap.
+ ///
+ public ICollection Keys => _dictionary.Keys;
+
+ ///
+ /// Gets a System.Collections.Generic.ICollection containing the values of the Multimap.
+ ///
+ public ICollection> Values => _dictionary.Values;
+
+ ///
+ /// Copy the items of the Multimap to an System.Array,
+ /// starting at a particular System.Array index.
+ ///
+ /// The one-dimensional System.Array that is the destination of the items copied
+ /// from Multimap. The System.Array must have zero-based indexing.
+ /// The zero-based index in array at which copying begins.
+ public void CopyTo(Array array, int index)
+ {
+ ((ICollection)_dictionary).CopyTo(array, index);
+ }
+
+ ///
+ /// Adds an item with the provided key and value to the Multimap.
+ ///
+ /// The object to use as the key of the item to add.
+ /// The object to use as the value of the item to add.
+ /// Thrown when couldn't add value to Multimap.
+ public void Add(TKey key, TValue value)
+ {
+ if (value != null)
+ {
+ if (_dictionary.TryGetValue(key, out var list))
+ {
+ list.Add(value);
+ }
+ else
+ {
+ list = new List { value };
+ if (!TryAdd(key, list))
+ throw new InvalidOperationException("Could not add value to Multimap.");
+ }
+ }
+ }
+
+ #endregion Public Members
+
+ #region Private Members
+
+ /**
+ * Helper method to encapsulate generator differences between dictionary types.
+ */
+ private bool TryRemove(TKey key, out IList value)
+ {
+ _dictionary.TryGetValue(key, out value);
+ return _dictionary.Remove(key);
+ }
+
+ /**
+ * Helper method to encapsulate generator differences between dictionary types.
+ */
+ private bool TryAdd(TKey key, IList value)
+ {
+ try
+ {
+ _dictionary.Add(key, value);
+ }
+ catch (ArgumentException)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ #endregion Private Members
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenAPIDateConverter.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenAPIDateConverter.mustache
new file mode 100644
index 00000000000..d7905102576
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/OpenAPIDateConverter.mustache
@@ -0,0 +1,21 @@
+{{>partial_header}}
+using Newtonsoft.Json.Converters;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339
+ /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types
+ ///
+ public class OpenAPIDateConverter : IsoDateTimeConverter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OpenAPIDateConverter()
+ {
+ // full-date = date-fullyear "-" date-month "-" date-mday
+ DateTimeFormat = "yyyy-MM-dd";
+ }
+ }
+}
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
new file mode 100644
index 00000000000..b15d2a25bf1
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/README.mustache
@@ -0,0 +1,270 @@
+# {{packageName}} - the C# library for the {{appName}}
+
+{{#appDescriptionWithNewLines}}
+{{{appDescriptionWithNewLines}}}
+{{/appDescriptionWithNewLines}}
+
+This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
+
+- 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:
+
+```
+nuget pack -Build -OutputDirectory out {{packageName}}.csproj
+```
+
+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.
+
+{{/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);
+```
+
+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));
+```
+
+{{/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}}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ReadOnlyDictionary.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ReadOnlyDictionary.mustache
new file mode 100644
index 00000000000..1299b2436be
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/ReadOnlyDictionary.mustache
@@ -0,0 +1,137 @@
+{{>partial_header}}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace {{packageName}}.Client
+{
+ public class ReadOnlyDictionary : IDictionary
+ {
+ private IDictionary _dictionaryImplementation;
+ public IEnumerator> GetEnumerator()
+ {
+ return _dictionaryImplementation.GetEnumerator();
+ }
+
+ public ReadOnlyDictionary()
+ {
+ _dictionaryImplementation = new Dictionary();
+ }
+
+ public ReadOnlyDictionary(IDictionary dictionaryImplementation)
+ {
+ if (dictionaryImplementation == null) throw new ArgumentNullException("dictionaryImplementation");
+ _dictionaryImplementation = dictionaryImplementation;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable) _dictionaryImplementation).GetEnumerator();
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+ }
+
+ public void Clear()
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return _dictionaryImplementation.Contains(item);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ _dictionaryImplementation.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+ }
+
+ public int Count
+ {
+ get { return _dictionaryImplementation.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public void Add(T key, K value)
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+ }
+
+ public bool ContainsKey(T key)
+ {
+ return _dictionaryImplementation.ContainsKey(key);
+ }
+
+ public bool Remove(T key)
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+ }
+
+ public bool TryGetValue(T key, out K value)
+ {
+ return _dictionaryImplementation.TryGetValue(key, out value);
+ }
+
+ public K this[T key]
+ {
+ get { return _dictionaryImplementation[key]; }
+ set
+ {
+ throw new ReadonlyOperationException("This instance is readonly.");
+
+ }
+ }
+
+ public ICollection Keys
+ {
+ get { return _dictionaryImplementation.Keys; }
+ }
+
+ public ICollection Values
+ {
+ get { return _dictionaryImplementation.Values; }
+ }
+ }
+
+ [Serializable]
+ public class ReadonlyOperationException : Exception
+ {
+ //
+ // For guidelines regarding the creation of new exception types, see
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
+ // and
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
+ //
+
+ public ReadonlyOperationException()
+ {
+ }
+
+ public ReadonlyOperationException(string message) : base(message)
+ {
+ }
+
+ public ReadonlyOperationException(string message, Exception inner) : base(message, inner)
+ {
+ }
+
+ protected ReadonlyOperationException(
+ SerializationInfo info,
+ StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RequestOptions.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RequestOptions.mustache
new file mode 100644
index 00000000000..dc924c733c1
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RequestOptions.mustache
@@ -0,0 +1,66 @@
+{{>partial_header}}
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// A container for generalized request inputs. This type allows consumers to extend the request functionality
+ /// by abstracting away from the default (built-in) request framework (e.g. RestSharp).
+ ///
+ public class RequestOptions
+ {
+ ///
+ /// Parameters to be bound to path parts of the Request's URL
+ ///
+ public Dictionary PathParameters { get; set; }
+
+ ///
+ /// Query parameters to be applied to the request.
+ /// Keys may have 1 or more values associated.
+ ///
+ public Multimap QueryParameters { get; set; }
+
+ ///
+ /// Header parameters to be applied to to the request.
+ /// Keys may have 1 or more values associated.
+ ///
+ public Multimap HeaderParameters { get; set; }
+
+ ///
+ /// Form parameters to be sent along with the request.
+ ///
+ public Dictionary FormParameters { get; set; }
+
+ ///
+ /// File parameters to be sent along with the request.
+ ///
+ public Dictionary FileParameters { get; set; }
+
+ ///
+ /// Cookies to be sent along with the request.
+ ///
+ public List Cookies { get; set; }
+
+ ///
+ /// Any data associated with a request body.
+ ///
+ public Object Data { get; set; }
+
+ ///
+ /// Constructs a new instance of
+ ///
+ public RequestOptions()
+ {
+ PathParameters = new Dictionary();
+ QueryParameters = new Multimap();
+ HeaderParameters = new Multimap();
+ FormParameters = new Dictionary();
+ FileParameters = new Dictionary();
+ Cookies = new List();
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RetryConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RetryConfiguration.mustache
new file mode 100644
index 00000000000..29aa6092681
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/RetryConfiguration.mustache
@@ -0,0 +1,39 @@
+using Polly;
+{{#useRestSharp}}
+using RestSharp;
+{{/useRestSharp}}
+{{#useHttpClient}}
+using System.Net.Http;
+{{/useHttpClient}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Configuration class to set the polly retry policies to be applied to the requests.
+ ///
+ public class RetryConfiguration
+ {
+{{#useRestSharp}}
+ ///
+ /// Retry policy
+ ///
+ public static Policy RetryPolicy { get; set; }
+
+ ///
+ /// Async retry policy
+ ///
+ public static AsyncPolicy AsyncRetryPolicy { get; set; }
+{{/useRestSharp}}
+{{#useHttpClient}}
+ ///
+ /// Retry policy
+ ///
+ public static Policy RetryPolicy { get; set; }
+
+ ///
+ /// Async retry policy
+ ///
+ public static AsyncPolicy AsyncRetryPolicy { get; set; }
+{{/useHttpClient}}
+ }
+}
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
new file mode 100644
index 00000000000..112cc3dc405
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/Solution.mustache
@@ -0,0 +1,27 @@
+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}}"
+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
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/TestProject.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/TestProject.mustache
new file mode 100644
index 00000000000..b1a50ae778e
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/TestProject.mustache
@@ -0,0 +1,36 @@
+
+
+
+
+ false
+ Properties
+ {{testPackageName}}
+ {{testPackageName}}
+ {{testTargetFramework}}
+ false
+ 512
+
+
+
+
+
+
+
+
+ {{packageGuid}}
+ {{packageName}}
+
+
+
+
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/WebRequestPathBuilder.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/WebRequestPathBuilder.mustache
new file mode 100644
index 00000000000..8175f0e5757
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/WebRequestPathBuilder.mustache
@@ -0,0 +1,44 @@
+{{>partial_header}}
+using System.Collections.Generic;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// A URI builder
+ ///
+ class WebRequestPathBuilder
+ {
+ private string _baseUrl;
+ private string _path;
+ private string _query = "?";
+ public WebRequestPathBuilder(string baseUrl, string path)
+ {
+ _baseUrl = baseUrl;
+ _path = path;
+ }
+
+ public void AddPathParameters(Dictionary parameters)
+ {
+ foreach (var parameter in parameters)
+ {
+ _path = _path.Replace("{" + parameter.Key + "}", parameter.Value);
+ }
+ }
+
+ public void AddQueryParameters(Multimap parameters)
+ {
+ foreach (var parameter in parameters)
+ {
+ foreach (var value in parameter.Value)
+ {
+ _query = _query + parameter.Key + "=" + value + "&";
+ }
+ }
+ }
+
+ public string GetFullUri()
+ {
+ return _baseUrl + _path + _query.Substring(0, _query.Length - 1);
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api.mustache
new file mode 100644
index 00000000000..8d84eecefb4
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api.mustache
@@ -0,0 +1,626 @@
+{{>partial_header}}
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Net;
+using System.Net.Mime;
+using {{packageName}}.Client;
+{{#hasImport}}using {{packageName}}.{{modelPackage}};
+{{/hasImport}}
+
+namespace {{packageName}}.{{apiPackage}}
+{
+ {{#operations}}
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{classname}}Sync : IApiAccessor
+ {
+ #region Synchronous Operations
+ {{#operation}}
+ ///
+ /// {{summary}}
+ ///
+ {{#notes}}
+ ///
+ /// {{notes}}
+ ///
+ {{/notes}}
+ /// Thrown when fails to make API call
+ {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}}
+ {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}});
+
+ ///
+ /// {{summary}}
+ ///
+ ///
+ /// {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}/// ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}}
+ ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}});
+ {{/operation}}
+ #endregion Synchronous Operations
+ }
+
+ {{#supportsAsync}}
+ ///
+ /// Represents a collection of functions to interact with the API endpoints
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{classname}}Async : IApiAccessor
+ {
+ #region Asynchronous Operations
+ {{#operation}}
+ ///
+ /// {{summary}}
+ ///
+ ///
+ /// {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}
+ /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}
+ /// Cancellation Token to cancel the request.
+ /// Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}
+ {{#returnType}}System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+
+ ///
+ /// {{summary}}
+ ///
+ ///
+ /// {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}
+ /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}
+ /// Cancellation Token to cancel the request.
+ /// Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}}
+ System.Threading.Tasks.Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
+ {{/operation}}
+ #endregion Asynchronous Operations
+ }
+ {{/supportsAsync}}
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{classname}} : {{interfacePrefix}}{{classname}}Sync{{#supportsAsync}}, {{interfacePrefix}}{{classname}}Async{{/supportsAsync}}
+ {
+
+ }
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints
+ ///
+ {{>visibility}} partial class {{classname}} : {{interfacePrefix}}{{classname}}
+ {
+ private {{packageName}}.Client.ExceptionFactory _exceptionFactory = (name, response) => null;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public {{classname}}() : this((string)null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public {{classname}}(string basePath)
+ {
+ this.Configuration = {{packageName}}.Client.Configuration.MergeConfigurations(
+ {{packageName}}.Client.GlobalConfiguration.Instance,
+ new {{packageName}}.Client.Configuration { BasePath = basePath }
+ );
+ this.Client = new {{packageName}}.Client.ApiClient(this.Configuration.BasePath);
+ {{#supportsAsync}}
+ this.AsynchronousClient = new {{packageName}}.Client.ApiClient(this.Configuration.BasePath);
+ {{/supportsAsync}}
+ this.ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// using Configuration object
+ ///
+ /// An instance of Configuration
+ ///
+ public {{classname}}({{packageName}}.Client.Configuration configuration)
+ {
+ if (configuration == null) throw new ArgumentNullException("configuration");
+
+ this.Configuration = {{packageName}}.Client.Configuration.MergeConfigurations(
+ {{packageName}}.Client.GlobalConfiguration.Instance,
+ configuration
+ );
+ this.Client = new {{packageName}}.Client.ApiClient(this.Configuration.BasePath);
+ {{#supportsAsync}}
+ this.AsynchronousClient = new {{packageName}}.Client.ApiClient(this.Configuration.BasePath);
+ {{/supportsAsync}}
+ ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// using a Configuration object and client instance.
+ ///
+ /// The client interface for synchronous API access.{{#supportsAsync}}
+ /// The client interface for asynchronous API access.{{/supportsAsync}}
+ /// The configuration object.
+ public {{classname}}({{packageName}}.Client.ISynchronousClient client, {{#supportsAsync}}{{packageName}}.Client.IAsynchronousClient asyncClient, {{/supportsAsync}}{{packageName}}.Client.IReadableConfiguration configuration)
+ {
+ if (client == null) throw new ArgumentNullException("client");
+ {{#supportsAsync}}
+ if (asyncClient == null) throw new ArgumentNullException("asyncClient");
+ {{/supportsAsync}}
+ if (configuration == null) throw new ArgumentNullException("configuration");
+
+ this.Client = client;
+ {{#supportsAsync}}
+ this.AsynchronousClient = asyncClient;
+ {{/supportsAsync}}
+ this.Configuration = configuration;
+ this.ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory;
+ }
+
+ {{#supportsAsync}}
+ ///
+ /// The client for accessing this underlying API asynchronously.
+ ///
+ public {{packageName}}.Client.IAsynchronousClient AsynchronousClient { get; set; }
+ {{/supportsAsync}}
+
+ ///
+ /// The client for accessing this underlying API synchronously.
+ ///
+ public {{packageName}}.Client.ISynchronousClient Client { get; set; }
+
+ ///
+ /// Gets the base path of the API client.
+ ///
+ /// The base path
+ public string GetBasePath()
+ {
+ return this.Configuration.BasePath;
+ }
+
+ ///
+ /// Gets or sets the configuration object
+ ///
+ /// An instance of the Configuration
+ public {{packageName}}.Client.IReadableConfiguration Configuration { get; set; }
+
+ ///
+ /// Provides a factory method hook for the creation of exceptions.
+ ///
+ public {{packageName}}.Client.ExceptionFactory ExceptionFactory
+ {
+ get
+ {
+ if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1)
+ {
+ throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported.");
+ }
+ return _exceptionFactory;
+ }
+ set { _exceptionFactory = value; }
+ }
+
+ {{#operation}}
+ ///
+ /// {{summary}} {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}}
+ public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
+ {
+ {{#returnType}}{{packageName}}.Client.ApiResponse<{{{returnType}}}> localVarResponse = {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+ return localVarResponse.Data;{{/returnType}}{{^returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{/returnType}}
+ }
+
+ ///
+ /// {{summary}} {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}/// ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}}
+ public {{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
+ {
+ {{#allParams}}
+ {{#required}}
+ {{^vendorExtensions.x-csharp-value-type}}
+ // verify the required parameter '{{paramName}}' is set
+ if ({{paramName}} == null)
+ throw new {{packageName}}.Client.ApiException(400, "Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}");
+
+ {{/vendorExtensions.x-csharp-value-type}}
+ {{/required}}
+ {{/allParams}}
+ {{packageName}}.Client.RequestOptions localVarRequestOptions = new {{packageName}}.Client.RequestOptions();
+
+ string[] _contentTypes = new string[] {
+ {{#consumes}}
+ "{{{mediaType}}}"{{^-last}},{{/-last}}
+ {{/consumes}}
+ };
+
+ // to determine the Accept header
+ string[] _accepts = new string[] {
+ {{#produces}}
+ "{{{mediaType}}}"{{^-last}},{{/-last}}
+ {{/produces}}
+ };
+
+ var localVarContentType = {{packageName}}.Client.ClientUtils.SelectHeaderContentType(_contentTypes);
+ if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType);
+
+ var localVarAccept = {{packageName}}.Client.ClientUtils.SelectHeaderAccept(_accepts);
+ if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept);
+
+ {{#pathParams}}
+ {{#required}}
+ localVarRequestOptions.PathParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // path parameter
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ localVarRequestOptions.PathParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // path parameter
+ }
+ {{/required}}
+ {{/pathParams}}
+ {{#queryParams}}
+ {{#required}}
+ {{#isDeepObject}}
+ {{#items.vars}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}.{{name}}));
+ {{/items.vars}}
+ {{^items}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("deepObject", "{{baseName}}", {{paramName}}));
+ {{/items}}
+ {{/isDeepObject}}
+ {{^isDeepObject}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));
+ {{/isDeepObject}}
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ {{#isDeepObject}}
+ {{#items.vars}}
+ if ({{paramName}}.{{name}} != null)
+ {
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}.{{name}}));
+ }
+ {{/items.vars}}
+ {{^items}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("deepObject", "{{baseName}}", {{paramName}}));
+ {{/items}}
+ {{/isDeepObject}}
+ {{^isDeepObject}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));
+ {{/isDeepObject}}
+ }
+ {{/required}}
+ {{/queryParams}}
+ {{#headerParams}}
+ {{#required}}
+ localVarRequestOptions.HeaderParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // header parameter
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ localVarRequestOptions.HeaderParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // header parameter
+ }
+ {{/required}}
+ {{/headerParams}}
+ {{#formParams}}
+ {{#required}}
+ {{#isFile}}
+ localVarRequestOptions.FileParameters.Add("{{baseName}}", {{paramName}});
+ {{/isFile}}
+ {{^isFile}}
+ localVarRequestOptions.FormParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // form parameter
+ {{/isFile}}
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ {{#isFile}}
+ localVarRequestOptions.FileParameters.Add("{{baseName}}", {{paramName}});
+ {{/isFile}}
+ {{^isFile}}
+ localVarRequestOptions.FormParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // form parameter
+ {{/isFile}}
+ }
+ {{/required}}
+ {{/formParams}}
+ {{#bodyParam}}
+ localVarRequestOptions.Data = {{paramName}};
+ {{/bodyParam}}
+
+ {{#authMethods}}
+ // authentication ({{name}}) required
+ {{#isApiKey}}
+ {{#isKeyInCookie}}
+ // cookie parameter support
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.Cookies.Add(new Cookie("{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")));
+ }
+ {{/isKeyInCookie}}
+ {{#isKeyInHeader}}
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.HeaderParameters.Add("{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"));
+ }
+ {{/isKeyInHeader}}
+ {{#isKeyInQuery}}
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("", "{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")));
+ }
+ {{/isKeyInQuery}}
+ {{/isApiKey}}
+ {{#isBasicBasic}}
+ // http basic authentication required
+ if (!string.IsNullOrEmpty(this.Configuration.Username) || !string.IsNullOrEmpty(this.Configuration.Password))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Basic " + {{packageName}}.Client.ClientUtils.Base64Encode(this.Configuration.Username + ":" + this.Configuration.Password));
+ }
+ {{/isBasicBasic}}
+ {{#isBasicBearer}}
+ // bearer authentication required
+ if (!string.IsNullOrEmpty(this.Configuration.AccessToken))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken);
+ }
+ {{/isBasicBearer}}
+ {{#isOAuth}}
+ // oauth required
+ if (!string.IsNullOrEmpty(this.Configuration.AccessToken))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken);
+ }
+ {{/isOAuth}}
+ {{#isHttpSignature}}
+ if (this.Configuration.HttpSigningConfiguration != null)
+ {
+ var HttpSigningHeaders = this.Configuration.HttpSigningConfiguration.GetHttpSignedHeader(this.Configuration.BasePath, "{{{httpMethod}}}", "{{{path}}}", localVarRequestOptions);
+ foreach (var headerItem in HttpSigningHeaders)
+ {
+ if (localVarRequestOptions.HeaderParameters.ContainsKey(headerItem.Key))
+ {
+ localVarRequestOptions.HeaderParameters[headerItem.Key] = new List() { headerItem.Value };
+ }
+ else
+ {
+ localVarRequestOptions.HeaderParameters.Add(headerItem.Key, headerItem.Value);
+ }
+ }
+ }
+ {{/isHttpSignature}}
+ {{/authMethods}}
+
+ // make the HTTP request
+ var localVarResponse = this.Client.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>("{{{path}}}", localVarRequestOptions, this.Configuration);
+
+ if (this.ExceptionFactory != null)
+ {
+ Exception _exception = this.ExceptionFactory("{{operationId}}", localVarResponse);
+ if (_exception != null) throw _exception;
+ }
+
+ return localVarResponse;
+ }
+
+ {{#supportsAsync}}
+ ///
+ /// {{summary}} {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}
+ /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}
+ /// Cancellation Token to cancel the request.
+ /// Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}
+ {{#returnType}}public async System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}public async System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ {{#returnType}}{{packageName}}.Client.ApiResponse<{{{returnType}}}> localVarResponse = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);
+ return localVarResponse.Data;{{/returnType}}{{^returnType}}await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);{{/returnType}}
+ }
+
+ ///
+ /// {{summary}} {{notes}}
+ ///
+ /// Thrown when fails to make API call
+ {{#allParams}}
+ /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
+ {{/allParams}}
+ /// Cancellation Token to cancel the request.
+ /// Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}}
+ public async System.Threading.Tasks.Task<{{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
+ {
+ {{#allParams}}
+ {{#required}}
+ {{^vendorExtensions.x-csharp-value-type}}
+ // verify the required parameter '{{paramName}}' is set
+ if ({{paramName}} == null)
+ throw new {{packageName}}.Client.ApiException(400, "Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}");
+
+ {{/vendorExtensions.x-csharp-value-type}}
+ {{/required}}
+ {{/allParams}}
+
+ {{packageName}}.Client.RequestOptions localVarRequestOptions = new {{packageName}}.Client.RequestOptions();
+
+ string[] _contentTypes = new string[] {
+ {{#consumes}}
+ "{{{mediaType}}}"{{^-last}}, {{/-last}}
+ {{/consumes}}
+ };
+
+ // to determine the Accept header
+ string[] _accepts = new string[] {
+ {{#produces}}
+ "{{{mediaType}}}"{{^-last}},{{/-last}}
+ {{/produces}}
+ };
+
+
+ var localVarContentType = {{packageName}}.Client.ClientUtils.SelectHeaderContentType(_contentTypes);
+ if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType);
+
+ var localVarAccept = {{packageName}}.Client.ClientUtils.SelectHeaderAccept(_accepts);
+ if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept);
+
+ {{#pathParams}}
+ {{#required}}
+ localVarRequestOptions.PathParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // path parameter
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ localVarRequestOptions.PathParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // path parameter
+ }
+ {{/required}}
+ {{/pathParams}}
+ {{#queryParams}}
+ {{#required}}
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));
+ }
+ {{/required}}
+ {{/queryParams}}
+ {{#headerParams}}
+ {{#required}}
+ localVarRequestOptions.HeaderParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // header parameter
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ localVarRequestOptions.HeaderParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // header parameter
+ }
+ {{/required}}
+ {{/headerParams}}
+ {{#formParams}}
+ {{#required}}
+ {{#isFile}}
+ localVarRequestOptions.FileParameters.Add("{{baseName}}", {{paramName}});
+ {{/isFile}}
+ {{^isFile}}
+ localVarRequestOptions.FormParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // form parameter
+ {{/isFile}}
+ {{/required}}
+ {{^required}}
+ if ({{paramName}} != null)
+ {
+ {{#isFile}}
+ localVarRequestOptions.FileParameters.Add("{{baseName}}", {{paramName}});
+ {{/isFile}}
+ {{^isFile}}
+ localVarRequestOptions.FormParameters.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})); // form parameter
+ {{/isFile}}
+ }
+ {{/required}}
+ {{/formParams}}
+ {{#bodyParam}}
+ localVarRequestOptions.Data = {{paramName}};
+ {{/bodyParam}}
+
+ {{#authMethods}}
+ // authentication ({{name}}) required
+ {{#isApiKey}}
+ {{#isKeyInCookie}}
+ // cookie parameter support
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.Cookies.Add(new Cookie("{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")));
+ }
+ {{/isKeyInCookie}}
+ {{#isKeyInHeader}}
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.HeaderParameters.Add("{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}"));
+ }
+ {{/isKeyInHeader}}
+ {{#isKeyInQuery}}
+ if (!string.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")))
+ {
+ localVarRequestOptions.QueryParameters.Add({{packageName}}.Client.ClientUtils.ParameterToMultiMap("", "{{keyParamName}}", this.Configuration.GetApiKeyWithPrefix("{{keyParamName}}")));
+ }
+ {{/isKeyInQuery}}
+ {{/isApiKey}}
+ {{#isBasic}}
+ {{#isBasicBasic}}
+ // http basic authentication required
+ if (!string.IsNullOrEmpty(this.Configuration.Username) || !string.IsNullOrEmpty(this.Configuration.Password))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Basic " + {{packageName}}.Client.ClientUtils.Base64Encode(this.Configuration.Username + ":" + this.Configuration.Password));
+ }
+ {{/isBasicBasic}}
+ {{#isBasicBearer}}
+ // bearer authentication required
+ if (!string.IsNullOrEmpty(this.Configuration.AccessToken))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken);
+ }
+ {{/isBasicBearer}}
+ {{/isBasic}}
+ {{#isOAuth}}
+ // oauth required
+ if (!string.IsNullOrEmpty(this.Configuration.AccessToken))
+ {
+ localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken);
+ }
+ {{/isOAuth}}
+ {{#isHttpSignature}}
+ if (this.Configuration.HttpSigningConfiguration != null)
+ {
+ var HttpSigningHeaders = this.Configuration.HttpSigningConfiguration.GetHttpSignedHeader(this.Configuration.BasePath, "{{{httpMethod}}}", "{{{path}}}", localVarRequestOptions);
+ foreach (var headerItem in HttpSigningHeaders)
+ {
+ if (localVarRequestOptions.HeaderParameters.ContainsKey(headerItem.Key))
+ {
+ localVarRequestOptions.HeaderParameters[headerItem.Key] = new List() { headerItem.Value };
+ }
+ else
+ {
+ localVarRequestOptions.HeaderParameters.Add(headerItem.Key, headerItem.Value);
+ }
+ }
+ }
+ {{/isHttpSignature}}
+ {{/authMethods}}
+
+ // make the HTTP request
+
+ var localVarResponse = await this.AsynchronousClient.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}Async<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>("{{{path}}}", localVarRequestOptions, this.Configuration, cancellationToken).ConfigureAwait(false);
+
+ if (this.ExceptionFactory != null)
+ {
+ Exception _exception = this.ExceptionFactory("{{operationId}}", localVarResponse);
+ if (_exception != null) throw _exception;
+ }
+
+ return localVarResponse;
+ }
+
+ {{/supportsAsync}}
+ {{/operation}}
+ }
+ {{/operations}}
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_doc.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_doc.mustache
new file mode 100644
index 00000000000..d12ee22ea38
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_doc.mustache
@@ -0,0 +1,134 @@
+# {{packageName}}.{{apiPackage}}.{{classname}}{{#description}}
+{{description}}{{/description}}
+
+All URIs are relative to *{{{basePath}}}*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
+{{/operation}}{{/operations}}
+
+{{#operations}}
+{{#operation}}
+
+# **{{{operationId}}}**
+> {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
+
+{{{summary}}}{{#notes}}
+
+{{{notes}}}{{/notes}}
+
+### Example
+```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()
+ {
+ 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.AddApiKey("{{{keyParamName}}}", "YOUR_API_KEY");
+ // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
+ // config.AddApiKeyPrefix("{{{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);
+ }
+ }
+ }
+}
+```
+
+### Parameters
+{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}
+{{#allParams}} **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{#isContainer}}{{baseType}}{{/isContainer}}{{^isContainer}}{{dataType}}{{/isContainer}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
+{{/allParams}}
+
+### Return type
+
+{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}}
+
+### Authorization
+
+{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}}
+
+### HTTP request headers
+
+ - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
+ - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}}
+
+{{#responses.0}}
+
+### HTTP response details
+| Status code | Description | Response headers |
+|-------------|-------------|------------------|
+{{#responses}}
+| **{{code}}** | {{message}} | {{#headers}} * {{baseName}} - {{description}} {{/headers}}{{^headers.0}} - {{/headers.0}} |
+{{/responses}}
+{{/responses.0}}
+
+[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
+
+{{/operation}}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_test.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_test.mustache
new file mode 100644
index 00000000000..02ca26b5f16
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/api_test.mustache
@@ -0,0 +1,73 @@
+{{>partial_header}}
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reflection;
+{{#useRestSharp}}
+using RestSharp;
+{{/useRestSharp}}
+using Xunit;
+
+using {{packageName}}.Client;
+using {{packageName}}.{{apiPackage}};
+{{#hasImport}}
+// uncomment below to import models
+//using {{packageName}}.{{modelPackage}};
+{{/hasImport}}
+
+namespace {{packageName}}.Test.Api
+{
+ ///
+ /// Class for testing {{classname}}
+ ///
+ ///
+ /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech).
+ /// Please update the test case below to test the API endpoint.
+ ///
+ public class {{classname}}Tests : IDisposable
+ {
+ private {{classname}} instance;
+
+ public {{classname}}Tests()
+ {
+ instance = new {{classname}}();
+ }
+
+ public void Dispose()
+ {
+ // Cleanup when everything is done.
+ }
+
+ ///
+ /// Test an instance of {{classname}}
+ ///
+ [Fact]
+ public void {{operationId}}InstanceTest()
+ {
+ // TODO uncomment below to test 'IsType' {{classname}}
+ //Assert.IsType<{{classname}}>(instance);
+ }
+ {{#operations}}
+ {{#operation}}
+
+ ///
+ /// Test {{operationId}}
+ ///
+ [Fact]
+ public void {{operationId}}Test()
+ {
+ // TODO uncomment below to test the method and replace null with proper value
+ {{#allParams}}
+ //{{{dataType}}} {{paramName}} = null;
+ {{/allParams}}
+ //{{#returnType}}var response = {{/returnType}}instance.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
+ {{#returnType}}
+ //Assert.IsType<{{{returnType}}}>(response);
+ {{/returnType}}
+ }
+ {{/operation}}
+ {{/operations}}
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appveyor.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appveyor.mustache
new file mode 100644
index 00000000000..eb85fc2a85b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/appveyor.mustache
@@ -0,0 +1,9 @@
+# auto-generated by OpenAPI Generator (https://github.com/OpenAPITools/openapi-generator)
+#
+image: Visual Studio 2019
+clone_depth: 1
+build_script:
+- dotnet build -c Release
+- dotnet test -c Release
+after_build:
+- dotnet pack .\src\{{{packageName}}}\{{{packageName}}}.csproj -o ../../output -c Release --no-build
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/functions.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/functions.mustache
new file mode 100644
index 00000000000..4709282dead
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/functions.mustache
@@ -0,0 +1,28 @@
+{{>partial_header}}
+
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.WebJobs;
+using Microsoft.Azure.WebJobs.Extensions.Http;
+
+namespace {{packageName}}.{{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}})
+ {
+ var method = this.GetType().GetMethod("{{operationId}}");
+
+ return method != null
+ ? (await ((Task)method.Invoke(this, new object[] { req, context{{#allParams}}{{#isPathParam}}, {{/isPathParam}}{{/allParams}} })).ConfigureAwait(false))
+ : new StatusCodeResult((int)HttpStatusCode.NotImplemented);
+ }
+ {{/operation}}
+ }
+}
+ {{/operations}}
+
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/git_push.sh.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/git_push.sh.mustache
new file mode 100644
index 00000000000..8b3f689c912
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/git_push.sh.mustache
@@ -0,0 +1,58 @@
+#!/bin/sh
+# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
+#
+# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com"
+
+git_user_id=$1
+git_repo_id=$2
+release_note=$3
+git_host=$4
+
+if [ "$git_host" = "" ]; then
+ git_host="{{{gitHost}}}"
+ echo "[INFO] No command line input provided. Set \$git_host to $git_host"
+fi
+
+if [ "$git_user_id" = "" ]; then
+ git_user_id="{{{gitUserId}}}"
+ echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
+fi
+
+if [ "$git_repo_id" = "" ]; then
+ git_repo_id="{{{gitRepoId}}}"
+ echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
+fi
+
+if [ "$release_note" = "" ]; then
+ release_note="{{{releaseNote}}}"
+ echo "[INFO] No command line input provided. Set \$release_note to $release_note"
+fi
+
+# Initialize the local directory as a Git repository
+git init
+
+# Adds the files in the local repository and stages them for commit.
+git add .
+
+# Commits the tracked changes and prepares them to be pushed to a remote repository.
+git commit -m "$release_note"
+
+# Sets the new remote
+git_remote=`git remote`
+if [ "$git_remote" = "" ]; then # git remote not defined
+
+ if [ "$GIT_TOKEN" = "" ]; then
+ echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
+ git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
+ else
+ git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git
+ fi
+
+fi
+
+git pull origin master
+
+# Pushes (Forces) the changes in the local repository up to the remote repository
+echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
+git push origin master 2>&1 | grep -v 'To https'
+
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore.mustache
new file mode 100644
index 00000000000..1ee53850b84
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/gitignore.mustache
@@ -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/libraries/httpclient/ApiClient.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/libraries/httpclient/ApiClient.mustache
new file mode 100644
index 00000000000..e9c39ea9f6d
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore-functions/libraries/httpclient/ApiClient.mustache
@@ -0,0 +1,739 @@
+{{>partial_header}}
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters;
+using System.Text;
+using System.Threading;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+{{^netStandard}}
+using System.Web;
+{{/netStandard}}
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
+{{#useWebRequest}}
+using System.Net.Http;
+{{/useWebRequest}}
+using System.Net.Http;
+using System.Net.Http.Headers;
+{{#supportsRetry}}
+using Polly;
+{{/supportsRetry}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// To Serialize/Deserialize JSON using our custom logic, but only when ContentType is JSON.
+ ///
+ internal class CustomJsonCodec
+ {
+ private readonly IReadableConfiguration _configuration;
+ private static readonly string _contentType = "application/json";
+ private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ public CustomJsonCodec(IReadableConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public CustomJsonCodec(JsonSerializerSettings serializerSettings, IReadableConfiguration configuration)
+ {
+ _serializerSettings = serializerSettings;
+ _configuration = configuration;
+ }
+
+ ///
+ /// Serialize the object into a JSON string.
+ ///
+ /// Object to be serialized.
+ /// A JSON string.
+ public string Serialize(object obj)
+ {
+ if (obj != null && obj is {{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema)
+ {
+ // the object to be serialized is an oneOf/anyOf schema
+ return (({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema)obj).ToJson();
+ }
+ else
+ {
+ return JsonConvert.SerializeObject(obj, _serializerSettings);
+ }
+ }
+
+ public async Task Deserialize(HttpResponseMessage response)
+ {
+ var result = (T) await Deserialize(response, typeof(T));
+ return result;
+ }
+
+ ///
+ /// Deserialize the JSON string into a proper object.
+ ///
+ /// The HTTP response.
+ /// Object type.
+ /// Object representation of the JSON string.
+ internal async Task Deserialize(HttpResponseMessage response, Type type)
+ {
+ IList headers = response.Headers.Select(x => x.Key + "=" + x.Value).ToList();
+
+ if (type == typeof(byte[])) // return byte array
+ {
+ return await response.Content.ReadAsByteArrayAsync();
+ }
+
+ // TODO: ? if (type.IsAssignableFrom(typeof(Stream)))
+ if (type == typeof(Stream))
+ {
+ var bytes = await response.Content.ReadAsByteArrayAsync();
+ if (headers != null)
+ {
+ var filePath = string.IsNullOrEmpty(_configuration.TempFolderPath)
+ ? Path.GetTempPath()
+ : _configuration.TempFolderPath;
+ var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$");
+ foreach (var header in headers)
+ {
+ var match = regex.Match(header.ToString());
+ if (match.Success)
+ {
+ string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
+ File.WriteAllBytes(fileName, bytes);
+ return new FileStream(fileName, FileMode.Open);
+ }
+ }
+ }
+ var stream = new MemoryStream(bytes);
+ return stream;
+ }
+
+ if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
+ {
+ return DateTime.Parse(await response.Content.ReadAsStringAsync(), null, System.Globalization.DateTimeStyles.RoundtripKind);
+ }
+
+ if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
+ {
+ return Convert.ChangeType(await response.Content.ReadAsStringAsync(), type);
+ }
+
+ // at this point, it must be a model (json)
+ try
+ {
+ return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync(), type, _serializerSettings);
+ }
+ catch (Exception e)
+ {
+ throw new ApiException(500, e.Message);
+ }
+ }
+
+ public string RootElement { get; set; }
+ public string Namespace { get; set; }
+ public string DateFormat { get; set; }
+
+ public string ContentType
+ {
+ get { return _contentType; }
+ set { throw new InvalidOperationException("Not allowed to set content type."); }
+ }
+ }
+ ///
+ /// Provides a default implementation of an Api client (both synchronous and asynchronous implementatios),
+ /// encapsulating general REST accessor use cases.
+ ///
+ ///
+ /// The Dispose method will manage the HttpClient lifecycle when not passed by constructor.
+ ///
+ {{>visibility}} partial class ApiClient : IDisposable, ISynchronousClient{{#supportsAsync}}, IAsynchronousClient{{/supportsAsync}}
+ {
+ private readonly string _baseUrl;
+
+ private readonly HttpClientHandler _httpClientHandler;
+ private readonly HttpClient _httpClient;
+ private readonly bool _disposeClient;
+
+ ///
+ /// Specifies the settings on a object.
+ /// These settings can be adjusted to accomodate custom serialization rules.
+ ///
+ public JsonSerializerSettings SerializerSettings { get; set; } = new JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
+ ContractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ ///
+ /// Initializes a new instance of the , defaulting to the global configurations' base url.
+ /// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
+ /// It's better to reuse the HttpClient and HttpClientHander .
+ ///
+ public ApiClient() :
+ this({{packageName}}.Client.GlobalConfiguration.Instance.BasePath)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the .
+ /// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
+ /// It's better to reuse the HttpClient and HttpClientHander .
+ ///
+ /// The target service's base path in URL format.
+ ///
+ public ApiClient(string basePath)
+ {
+ if (string.IsNullOrEmpty(basePath)) throw new ArgumentException("basePath cannot be empty");
+
+ _httpClientHandler = new HttpClientHandler();
+ _httpClient = new HttpClient(_httpClientHandler, true);
+ _disposeClient = true;
+ _baseUrl = basePath;
+ }
+
+ ///
+ /// Initializes a new instance of the , defaulting to the global configurations' base url.
+ ///
+ /// An instance of HttpClient.
+ /// An optional instance of HttpClientHandler that is used by HttpClient.
+ ///
+ ///
+ /// Some configuration settings will not be applied without passing an HttpClientHandler.
+ /// The features affected are: Setting and Retrieving Cookies, Client Certificates, Proxy settings.
+ ///
+ public ApiClient(HttpClient client, HttpClientHandler handler = null) :
+ this(client, {{packageName}}.Client.GlobalConfiguration.Instance.BasePath, handler)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// An instance of HttpClient.
+ /// The target service's base path in URL format.
+ /// An optional instance of HttpClientHandler that is used by HttpClient.
+ ///
+ ///
+ ///
+ /// Some configuration settings will not be applied without passing an HttpClientHandler.
+ /// The features affected are: Setting and Retrieving Cookies, Client Certificates, Proxy settings.
+ ///
+ public ApiClient(HttpClient client, string basePath, HttpClientHandler handler = null)
+ {
+ if (client == null) throw new ArgumentNullException("client cannot be null");
+ if (string.IsNullOrEmpty(basePath)) throw new ArgumentException("basePath cannot be empty");
+
+ _httpClientHandler = handler;
+ _httpClient = client;
+ _baseUrl = basePath;
+ }
+
+ ///
+ /// Disposes resources if they were created by us
+ ///
+ public void Dispose()
+ {
+ if(_disposeClient) {
+ _httpClient.Dispose();
+ }
+ }
+
+ /// Prepares multipart/form-data content
+ {{! TODO: Add handling of improper usage }}
+ HttpContent PrepareMultipartFormDataContent(RequestOptions options)
+ {
+ string boundary = "---------" + Guid.NewGuid().ToString().ToUpperInvariant();
+ var multipartContent = new MultipartFormDataContent(boundary);
+ foreach (var formParameter in options.FormParameters)
+ {
+ multipartContent.Add(new StringContent(formParameter.Value), formParameter.Key);
+ }
+
+ if (options.FileParameters != null && options.FileParameters.Count > 0)
+ {
+ foreach (var fileParam in options.FileParameters)
+ {
+ var content = new StreamContent(fileParam.Value.Content);
+ content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
+ multipartContent.Add(content, fileParam.Key,
+ fileParam.Value.Name);
+ }
+ }
+ return multipartContent;
+ }
+
+ ///
+ /// Provides all logic for constructing a new HttpRequestMessage.
+ /// At this point, all information for querying the service is known. Here, it is simply
+ /// mapped into the a HttpRequestMessage.
+ ///
+ /// The http verb.
+ /// The target path (or resource).
+ /// The additional request options.
+ /// A per-request configuration object. It is assumed that any merge with
+ /// GlobalConfiguration has been done before calling this method.
+ /// [private] A new HttpRequestMessage instance.
+ ///
+ private HttpRequestMessage NewRequest(
+ HttpMethod method,
+ string path,
+ RequestOptions options,
+ IReadableConfiguration configuration)
+ {
+ if (path == null) throw new ArgumentNullException("path");
+ if (options == null) throw new ArgumentNullException("options");
+ if (configuration == null) throw new ArgumentNullException("configuration");
+
+ WebRequestPathBuilder builder = new WebRequestPathBuilder(_baseUrl, path);
+
+ builder.AddPathParameters(options.PathParameters);
+
+ builder.AddQueryParameters(options.QueryParameters);
+
+ HttpRequestMessage request = new HttpRequestMessage(method, builder.GetFullUri());
+
+ if (configuration.UserAgent != null)
+ {
+ request.Headers.TryAddWithoutValidation("User-Agent", configuration.UserAgent);
+ }
+
+ if (configuration.DefaultHeaders != null)
+ {
+ foreach (var headerParam in configuration.DefaultHeaders)
+ {
+ request.Headers.Add(headerParam.Key, headerParam.Value);
+ }
+ }
+
+ if (options.HeaderParameters != null)
+ {
+ foreach (var headerParam in options.HeaderParameters)
+ {
+ foreach (var value in headerParam.Value)
+ {
+ // Todo make content headers actually content headers
+ request.Headers.TryAddWithoutValidation(headerParam.Key, value);
+ }
+ }
+ }
+
+ List> contentList = new List>();
+
+ string contentType = null;
+ if (options.HeaderParameters != null && options.HeaderParameters.ContainsKey("Content-Type"))
+ {
+ var contentTypes = options.HeaderParameters["Content-Type"];
+ contentType = contentTypes.FirstOrDefault();
+ }
+
+ {{!// TODO Add error handling in case of improper usage}}
+ if (contentType == "multipart/form-data")
+ {
+ request.Content = PrepareMultipartFormDataContent(options);
+ }
+ else if (contentType == "application/x-www-form-urlencoded")
+ {
+ request.Content = new FormUrlEncodedContent(options.FormParameters);
+ }
+ else
+ {
+ if (options.Data != null)
+ {
+ if (options.Data is FileParameter fp)
+ {
+ contentType = contentType ?? "application/octet-stream";
+
+ var streamContent = new StreamContent(fp.Content);
+ streamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
+ request.Content = streamContent;
+ }
+ else
+ {
+ var serializer = new CustomJsonCodec(SerializerSettings, configuration);
+ request.Content = new StringContent(serializer.Serialize(options.Data), new UTF8Encoding(),
+ "application/json");
+ }
+ }
+ }
+
+
+
+ // TODO provide an alternative that allows cookies per request instead of per API client
+ if (options.Cookies != null && options.Cookies.Count > 0)
+ {
+ request.Properties["CookieContainer"] = options.Cookies;
+ }
+
+ return request;
+ }
+
+ partial void InterceptRequest(HttpRequestMessage req);
+ partial void InterceptResponse(HttpRequestMessage req, HttpResponseMessage response);
+
+ private async Task> ToApiResponse(HttpResponseMessage response, object responseData, Uri uri)
+ {
+ T result = (T) responseData;
+ string rawContent = await response.Content.ReadAsStringAsync();
+
+ var transformed = new ApiResponse