From d9dcffc3701cd18ba9ec652d0010c2b6b10319f1 Mon Sep 17 00:00:00 2001
From: Kwok He <105217051+Kwok-he-Chu@users.noreply.github.com>
Date: Tue, 18 Nov 2025 16:32:20 +0100
Subject: [PATCH 1/4] Added /Core classes and tests in preparations for the
v7-generator-upgrade
---
Adyen/Adyen.csproj | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Adyen/Adyen.csproj b/Adyen/Adyen.csproj
index deeff46a8..3e50f1b5f 100644
--- a/Adyen/Adyen.csproj
+++ b/Adyen/Adyen.csproj
@@ -57,4 +57,8 @@
+
+
+
+
From 74b88ecc5960f4498cf9715ebd516d6d3ed4aa5d Mon Sep 17 00:00:00 2001
From: Kwok He <105217051+Kwok-he-Chu@users.noreply.github.com>
Date: Wed, 26 Nov 2025 16:24:26 +0100
Subject: [PATCH 2/4] Include updated core classes for review
---
Adyen/Adyen.csproj | 4 ----
1 file changed, 4 deletions(-)
diff --git a/Adyen/Adyen.csproj b/Adyen/Adyen.csproj
index 3e50f1b5f..deeff46a8 100644
--- a/Adyen/Adyen.csproj
+++ b/Adyen/Adyen.csproj
@@ -57,8 +57,4 @@
-
-
-
-
From d49b160bb86dc219ae659312ffb8ba40604eb597 Mon Sep 17 00:00:00 2001
From: Kwok He <105217051+Kwok-he-Chu@users.noreply.github.com>
Date: Tue, 18 Nov 2025 16:47:14 +0100
Subject: [PATCH 3/4] Add new mustache templates for v7
Remove old mustache templates
---
templates-v7/csharp/AssemblyInfo.mustache | 40 ++
.../csharp/NullConditionalParameter.mustache | 1 +
.../csharp/NullConditionalProperty.mustache | 1 +
.../csharp/OpenAPIDateConverter.mustache | 21 +
templates-v7/csharp/ValidateRegex.mustache | 6 +
.../csharp/auth/OAuthAuthenticator.mustache | 136 ++++
templates-v7/csharp/auth/OAuthFlow.mustache | 19 +
.../csharp/auth/TokenResponse.mustache | 24 +
.../generichost/ApiException.mustache | 47 ++
.../libraries/generichost/ApiFactory.mustache | 48 ++
.../generichost/ApiKeyToken.mustache | 53 ++
.../generichost/ApiResponseEventArgs.mustache | 24 +
.../generichost/ApiResponse`1.mustache | 202 ++++++
.../generichost/ApiTestsBase.mustache | 66 ++
.../libraries/generichost/AsModel.mustache | 3 +
.../generichost/BearerToken.mustache | 41 ++
.../generichost/ClientUtils.mustache | 385 ++++++++++
.../generichost/CookieContainer.mustache | 22 +
.../generichost/DateFormats.mustache | 2 +
.../DateOnlyJsonConverter.mustache | 52 ++
.../DateOnlyNullableJsonConverter.mustache | 57 ++
.../generichost/DateTimeFormats.mustache | 22 +
.../DateTimeJsonConverter.mustache | 52 ++
.../DateTimeNullableJsonConverter.mustache | 57 ++
.../DependencyInjectionTests.mustache | 211 ++++++
.../generichost/EnumValueDataType.mustache | 1 +
.../generichost/ExceptionEventArgs.mustache | 24 +
.../generichost/HmacKeyToken.mustache | 36 +
.../generichost/HostConfiguration.mustache | 178 +++++
.../HttpSigningConfiguration.mustache | 679 ++++++++++++++++++
.../generichost/HttpSigningToken.mustache | 45 ++
.../libraries/generichost/IApi.mustache | 13 +
.../IHostBuilderExtensions.mustache | 41 ++
.../IHttpClientBuilderExtensions.mustache | 75 ++
.../IServiceCollectionExtensions.mustache | 33 +
.../generichost/ImplementsIEquatable.mustache | 1 +
.../ImplementsValidatable.mustache | 1 +
.../generichost/JsonConverter.mustache | 661 +++++++++++++++++
.../JsonSerializerOptionsProvider.mustache | 29 +
.../generichost/ModelBaseSignature.mustache | 1 +
.../generichost/ModelSignature.mustache | 1 +
.../libraries/generichost/OAuthToken.mustache | 41 ++
.../OnDeserializationError.mustache | 2 +
.../generichost/OperationSignature.mustache | 1 +
.../libraries/generichost/Option.mustache | 48 ++
.../generichost/OptionProperty.mustache | 1 +
.../generichost/README.client.mustache | 140 ++++
.../SourceGenerationContext.mustache | 9 +
.../libraries/generichost/TokenBase.mustache | 73 ++
.../generichost/TokenContainer.mustache | 39 +
.../generichost/TokenProvider.mustache | 39 +
.../generichost/ValidateRegex.mustache | 7 +
.../generichost/WebhookHandler.mustache | 88 +++
.../generichost/WriteProperty.mustache | 10 +
.../generichost/WritePropertyHelper.mustache | 10 +
.../csharp/libraries/generichost/api.mustache | 653 +++++++++++++++++
.../libraries/generichost/api_doc.mustache | 91 +++
.../libraries/generichost/api_test.mustache | 71 ++
.../libraries/generichost/model.mustache | 56 ++
.../generichost/modelGeneric.mustache | 413 +++++++++++
.../generichost/modelInnerEnum.mustache | 116 +++
templates-v7/csharp/partial_header.mustache | 18 +
templates-v7/csharp/validatable.mustache | 141 ++++
templates-v7/csharp/visibility.mustache | 1 +
.../csharp/AbstractOpenAPISchema.mustache | 71 --
templates/csharp/api-single.mustache | 87 ---
templates/csharp/api.mustache | 86 ---
templates/csharp/api_invoke.mustache | 1 -
.../csharp/api_parameter_ordering.mustache | 1 -
.../api_parameter_ordering_required.mustache | 1 -
templates/csharp/api_parameters.mustache | 2 -
.../csharp/api_parameters_async.mustache | 2 -
templates/csharp/config.yaml | 6 -
.../csharp/method_documentation.mustache | 13 -
templates/csharp/model.mustache | 49 --
templates/csharp/modelGeneric.mustache | 384 ----------
templates/csharp/modelOneOf.mustache | 286 --------
templates/csharp/partial_header.mustache | 12 -
78 files changed, 5479 insertions(+), 1001 deletions(-)
create mode 100644 templates-v7/csharp/AssemblyInfo.mustache
create mode 100644 templates-v7/csharp/NullConditionalParameter.mustache
create mode 100644 templates-v7/csharp/NullConditionalProperty.mustache
create mode 100644 templates-v7/csharp/OpenAPIDateConverter.mustache
create mode 100644 templates-v7/csharp/ValidateRegex.mustache
create mode 100644 templates-v7/csharp/auth/OAuthAuthenticator.mustache
create mode 100644 templates-v7/csharp/auth/OAuthFlow.mustache
create mode 100644 templates-v7/csharp/auth/TokenResponse.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiException.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiFactory.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/AsModel.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/BearerToken.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ClientUtils.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/CookieContainer.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateFormats.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/HmacKeyToken.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/HostConfiguration.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/IApi.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/JsonConverter.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ModelSignature.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/OAuthToken.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/OperationSignature.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/Option.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/OptionProperty.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/README.client.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/TokenBase.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/TokenContainer.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/TokenProvider.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/ValidateRegex.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/WebhookHandler.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/WriteProperty.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/api.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/api_doc.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/api_test.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/model.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/modelGeneric.mustache
create mode 100644 templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache
create mode 100644 templates-v7/csharp/partial_header.mustache
create mode 100644 templates-v7/csharp/validatable.mustache
create mode 100644 templates-v7/csharp/visibility.mustache
delete mode 100644 templates/csharp/AbstractOpenAPISchema.mustache
delete mode 100644 templates/csharp/api-single.mustache
delete mode 100644 templates/csharp/api.mustache
delete mode 100644 templates/csharp/api_invoke.mustache
delete mode 100644 templates/csharp/api_parameter_ordering.mustache
delete mode 100644 templates/csharp/api_parameter_ordering_required.mustache
delete mode 100644 templates/csharp/api_parameters.mustache
delete mode 100644 templates/csharp/api_parameters_async.mustache
delete mode 100644 templates/csharp/config.yaml
delete mode 100644 templates/csharp/method_documentation.mustache
delete mode 100644 templates/csharp/model.mustache
delete mode 100644 templates/csharp/modelGeneric.mustache
delete mode 100644 templates/csharp/modelOneOf.mustache
delete mode 100644 templates/csharp/partial_header.mustache
diff --git a/templates-v7/csharp/AssemblyInfo.mustache b/templates-v7/csharp/AssemblyInfo.mustache
new file mode 100644
index 000000000..d5d937dc1
--- /dev/null
+++ b/templates-v7/csharp/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/templates-v7/csharp/NullConditionalParameter.mustache b/templates-v7/csharp/NullConditionalParameter.mustache
new file mode 100644
index 000000000..d8ad92666
--- /dev/null
+++ b/templates-v7/csharp/NullConditionalParameter.mustache
@@ -0,0 +1 @@
+{{#isNullable}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}{{/isNullable}}
\ No newline at end of file
diff --git a/templates-v7/csharp/NullConditionalProperty.mustache b/templates-v7/csharp/NullConditionalProperty.mustache
new file mode 100644
index 000000000..7dcafa803
--- /dev/null
+++ b/templates-v7/csharp/NullConditionalProperty.mustache
@@ -0,0 +1 @@
+{{#lambda.first}}{{#isNullable}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}}
\ No newline at end of file
diff --git a/templates-v7/csharp/OpenAPIDateConverter.mustache b/templates-v7/csharp/OpenAPIDateConverter.mustache
new file mode 100644
index 000000000..d79051025
--- /dev/null
+++ b/templates-v7/csharp/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/templates-v7/csharp/ValidateRegex.mustache b/templates-v7/csharp/ValidateRegex.mustache
new file mode 100644
index 000000000..d05be8a97
--- /dev/null
+++ b/templates-v7/csharp/ValidateRegex.mustache
@@ -0,0 +1,6 @@
+// {{{name}}} ({{{dataType}}}) pattern.
+Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}});
+if (!regex{{{name}}}.Match(this.{{{name}}}{{#isUuid}}.ToString(){{/isUuid}}).Success)
+{
+ yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" });
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/auth/OAuthAuthenticator.mustache b/templates-v7/csharp/auth/OAuthAuthenticator.mustache
new file mode 100644
index 000000000..4a2a3fa85
--- /dev/null
+++ b/templates-v7/csharp/auth/OAuthAuthenticator.mustache
@@ -0,0 +1,136 @@
+{{>partial_header}}
+
+using System;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using RestSharp;
+using RestSharp.Authenticators;
+
+namespace {{packageName}}.{{clientPackage}}.Auth
+{
+ ///
+ /// An authenticator for OAuth2 authentication flows
+ ///
+ public class OAuthAuthenticator : IAuthenticator
+ {
+ private TokenResponse{{nrt?}} _token;
+
+ ///
+ /// Returns the current authentication token. Can return null if there is no authentication token, or it has expired.
+ ///
+ public string{{nrt?}} Token
+ {
+ get
+ {
+ if (_token == null) return null;
+ if (_token.ExpiresIn == null) return _token.AccessToken;
+ if (_token.ExpiresAt < DateTime.Now) return null;
+
+ return _token.AccessToken;
+ }
+ }
+
+ readonly string _tokenUrl;
+ readonly string _clientId;
+ readonly string _clientSecret;
+ readonly string{{nrt?}} _scope;
+ readonly string _grantType;
+ readonly JsonSerializerSettings _serializerSettings;
+ readonly IReadableConfiguration _configuration;
+
+ ///
+ /// Initialize the OAuth2 Authenticator
+ ///
+ public OAuthAuthenticator(
+ string tokenUrl,
+ string clientId,
+ string clientSecret,
+ string{{nrt?}} scope,
+ OAuthFlow? flow,
+ JsonSerializerSettings serializerSettings,
+ IReadableConfiguration configuration)
+ {
+ _tokenUrl = tokenUrl;
+ _clientId = clientId;
+ _clientSecret = clientSecret;
+ _scope = scope;
+ _serializerSettings = serializerSettings;
+ _configuration = configuration;
+
+ switch (flow)
+ {
+ /*case OAuthFlow.ACCESS_CODE:
+ _grantType = "authorization_code";
+ break;
+ case OAuthFlow.IMPLICIT:
+ _grantType = "implicit";
+ break;
+ case OAuthFlow.PASSWORD:
+ _grantType = "password";
+ break;*/
+ case OAuthFlow.APPLICATION:
+ _grantType = "client_credentials";
+ break;
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// Creates an authentication parameter from an access token.
+ ///
+ /// An authentication parameter.
+ protected async ValueTask GetAuthenticationParameter()
+ {
+ var token = string.IsNullOrEmpty(Token) ? await GetToken().ConfigureAwait(false) : Token;
+ return new HeaderParameter(KnownHeaders.Authorization, token);
+ }
+
+ ///
+ /// Gets the token from the OAuth2 server.
+ ///
+ /// An authentication token.
+ async Task GetToken()
+ {
+ var client = new RestClient(_tokenUrl, configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(_serializerSettings, _configuration)));
+
+ var request = new RestRequest();
+ if (!string.IsNullOrWhiteSpace(_token?.RefreshToken))
+ {
+ request.AddParameter("grant_type", "refresh_token")
+ .AddParameter("refresh_token", _token.RefreshToken);
+ }
+ else
+ {
+ request
+ .AddParameter("grant_type", _grantType)
+ .AddParameter("client_id", _clientId)
+ .AddParameter("client_secret", _clientSecret);
+ }
+ if (!string.IsNullOrEmpty(_scope))
+ {
+ request.AddParameter("scope", _scope);
+ }
+ _token = await client.PostAsync(request).ConfigureAwait(false);
+ // RFC6749 - token_type is case insensitive.
+ // RFC6750 - In Authorization header Bearer should be capitalized.
+ // Fix the capitalization irrespective of token_type casing.
+ switch (_token?.TokenType?.ToLower())
+ {
+ case "bearer":
+ return $"Bearer {_token.AccessToken}";
+ default:
+ return $"{_token?.TokenType} {_token?.AccessToken}";
+ }
+ }
+
+ ///
+ /// Retrieves the authentication token (creating a new one if necessary) and adds it to the current request
+ ///
+ ///
+ ///
+ ///
+ public async ValueTask Authenticate(IRestClient client, RestRequest request)
+ => request.AddOrUpdateParameter(await GetAuthenticationParameter().ConfigureAwait(false));
+ }
+}
diff --git a/templates-v7/csharp/auth/OAuthFlow.mustache b/templates-v7/csharp/auth/OAuthFlow.mustache
new file mode 100644
index 000000000..0578a2d16
--- /dev/null
+++ b/templates-v7/csharp/auth/OAuthFlow.mustache
@@ -0,0 +1,19 @@
+{{>partial_header}}
+
+namespace {{packageName}}.{{clientPackage}}.Auth
+{
+ ///
+ /// Available flows for OAuth2 authentication
+ ///
+ public enum OAuthFlow
+ {
+ /// Authorization code flow
+ ACCESS_CODE,
+ /// Implicit flow
+ IMPLICIT,
+ /// Password flow
+ PASSWORD,
+ /// Client credentials flow
+ APPLICATION
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/auth/TokenResponse.mustache b/templates-v7/csharp/auth/TokenResponse.mustache
new file mode 100644
index 000000000..fc90d0c51
--- /dev/null
+++ b/templates-v7/csharp/auth/TokenResponse.mustache
@@ -0,0 +1,24 @@
+{{>partial_header}}
+
+using System;
+using Newtonsoft.Json;
+
+namespace {{packageName}}.{{clientPackage}}.Auth
+{
+ class TokenResponse
+ {
+ [JsonProperty("token_type")]
+ public string TokenType { get; set; }
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+ [JsonProperty("expires_in")]
+ public int? ExpiresIn { get; set; }
+ [JsonProperty("created")]
+ public DateTime? Created { get; set; }
+
+ [JsonProperty("refresh_token")]
+ public string{{nrt?}} RefreshToken { get; set; }
+
+ public DateTime? ExpiresAt => ExpiresIn == null ? null : Created?.AddSeconds(ExpiresIn.Value);
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ApiException.mustache b/templates-v7/csharp/libraries/generichost/ApiException.mustache
new file mode 100644
index 000000000..df9992b69
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiException.mustache
@@ -0,0 +1,47 @@
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+
+namespace {{packageName}}.{{corePackageName}}.{{clientPackage}}
+{
+ ///
+ /// API Exception
+ ///
+ {{>visibility}} class ApiException : Exception
+ {
+ ///
+ /// The reason the api request failed
+ ///
+ public string{{nrt?}} ReasonPhrase { get; }
+
+ ///
+ /// The HttpStatusCode
+ ///
+ public System.Net.HttpStatusCode StatusCode { get; }
+
+ ///
+ /// The raw data returned by the api
+ ///
+ public string RawContent { get; }
+
+ ///
+ /// Construct the ApiException from parts of the response
+ ///
+ ///
+ ///
+ ///
+ public ApiException(string{{nrt?}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent)
+ {
+ ReasonPhrase = reasonPhrase;
+
+ StatusCode = statusCode;
+
+ RawContent = rawContent;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/ApiFactory.mustache b/templates-v7/csharp/libraries/generichost/ApiFactory.mustache
new file mode 100644
index 000000000..873c8b355
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiFactory.mustache
@@ -0,0 +1,48 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace {{packageName}}.{{corePackageName}}.{{clientPackage}}
+{
+ ///
+ /// The factory interface for creating the services that can communicate with the {{packageName}} APIs.
+ ///
+ {{>visibility}} interface {{interfacePrefix}}ApiFactory
+ {
+ ///
+ /// A method to create an IApi of type IResult
+ ///
+ ///
+ ///
+ IResult Create() where IResult : {{interfacePrefix}}{{packageName}}ApiService;
+ }
+
+ ///
+ /// The implementation of .
+ ///
+ {{>visibility}} class ApiFactory : {{interfacePrefix}}ApiFactory
+ {
+ ///
+ /// The service provider
+ ///
+ public IServiceProvider Services { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public ApiFactory(IServiceProvider services)
+ {
+ Services = services;
+ }
+
+ ///
+ /// A method to create an IApi of type IResult
+ ///
+ ///
+ ///
+ public IResult Create() where IResult : {{interfacePrefix}}{{packageName}}ApiService
+ {
+ return Services.GetRequiredService();
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache b/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache
new file mode 100644
index 000000000..efc32707b
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache
@@ -0,0 +1,53 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{corePackageName}}.{{clientPackage}};
+
+namespace {{packageName}}.{{apiName}}.{{clientPackage}}
+{
+ ///
+ /// The `ADYEN_API_KEY` is an Adyen API authentication token that must be included in the HTTP request header and allows your application to securely communicate with the Adyen APIs.
+ /// Guide on how to obtain the `ADYEN_API_KEY`
+ /// 1. For Digital/ECOM & In-Person Payments, visit: https://docs.adyen.com/development-resources/api-credentials/#generate-api-key to get your API Key.
+ /// 2. For Platforms & Financial Services, visit: https://docs.adyen.com/adyen-for-platforms-model to get started.
+ ///
+ {{>visibility}} class ApiKeyToken : TokenBase
+ {
+ ///
+ /// The `ADYEN_API_KEY`.
+ ///
+ private readonly string _apiKeyValue;
+
+ ///
+ /// The name of the header.
+ ///
+ public ClientUtils.ApiKeyHeader Header { get; }
+
+ ///
+ /// Constructs the ApiKeyToken object with the API key value provided.
+ /// This can then be accessed using .
+ ///
+ /// Your Adyen API Key value.
+ /// The header name, retrieved from
+ /// Your prefix, default: empty.
+ public ApiKeyToken(string value, ClientUtils.ApiKeyHeader header, string prefix = "") : base()
+ {
+ Header = header;
+ _apiKeyValue = $"{prefix}{value}";
+ }
+
+ ///
+ /// Adds the token the HttpRequestMessage headers.
+ ///
+ /// .
+ public virtual void AddTokenToHttpRequestMessageHeader(global::System.Net.Http.HttpRequestMessage request)
+ {
+ request.Headers.Add(ClientUtils.ApiKeyHeaderToString(Header), _apiKeyValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache b/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache
new file mode 100644
index 000000000..a3de3f0d0
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache
@@ -0,0 +1,24 @@
+using System;
+
+namespace {{packageName}}.{{corePackageName}}.{{clientPackage}}
+{
+ ///
+ /// Useful for tracking server health
+ ///
+ {{>visibility}} class ApiResponseEventArgs : EventArgs
+ {
+ ///
+ /// The ApiResponse.
+ ///
+ public ApiResponse ApiResponse { get; }
+
+ ///
+ /// The ApiResponseEventArgs.
+ ///
+ ///
+ public ApiResponseEventArgs(ApiResponse apiResponse)
+ {
+ ApiResponse = apiResponse;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache b/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache
new file mode 100644
index 000000000..b78cbf1f4
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache
@@ -0,0 +1,202 @@
+//
+{{#nrt}}
+#nullable enable
+{{/nrt}}
+using System;
+{{^netStandard}}
+using System.Diagnostics.CodeAnalysis;
+{{/netStandard}}
+using System.Net;
+
+namespace {{packageName}}.{{corePackageName}}.{{clientPackage}}
+{
+ ///
+ /// Provides a non-generic contract for the ApiResponse wrapper.
+ ///
+ {{>visibility}} partial interface IApiResponse
+ {
+ ///
+ /// The IsSuccessStatusCode from the api response
+ ///
+ bool IsSuccessStatusCode { get; }
+
+ ///
+ /// Gets the status code (HTTP status code)
+ ///
+ /// The status code.
+ HttpStatusCode StatusCode { get; }
+
+ ///
+ /// The raw content of this response.
+ ///
+ string RawContent { get; }
+
+ ///
+ /// The raw binary stream (only set for binary responses)
+ ///
+ System.IO.Stream{{nrt?}} ContentStream { get; }
+
+ ///
+ /// The DateTime when the request was retrieved.
+ ///
+ DateTime DownloadedAt { get; }
+
+ ///
+ /// The headers contained in the api response
+ ///
+ System.Net.Http.Headers.HttpResponseHeaders Headers { get; }
+
+ ///
+ /// The path used when making the request.
+ ///
+ string Path { get; }
+
+ ///
+ /// The reason phrase contained in the api response
+ ///
+ string{{nrt?}} ReasonPhrase { get; }
+
+ ///
+ /// The DateTime when the request was sent.
+ ///
+ DateTime RequestedAt { get; }
+
+ ///
+ /// The Uri used when making the request.
+ ///
+ Uri{{nrt?}} RequestUri { get; }
+ }
+
+ ///
+ /// API Response
+ ///
+ {{>visibility}} partial class ApiResponse : IApiResponse
+ {
+ ///
+ /// Gets the status code (HTTP status code).
+ ///
+ /// The status code.
+ public HttpStatusCode StatusCode { get; }
+
+ ///
+ /// The raw data.
+ ///
+ public string RawContent { get; protected set; }
+
+ ///
+ /// The raw binary stream (only set for binary responses).
+ ///
+ public System.IO.Stream{{nrt?}} ContentStream { get; protected set; }
+
+ ///
+ /// The IsSuccessStatusCode from the api response.
+ ///
+ public bool IsSuccessStatusCode { get; }
+
+ ///
+ /// The reason phrase contained in the api response.
+ ///
+ public string{{nrt?}} ReasonPhrase { get; }
+
+ ///
+ /// The headers contained in the api response.
+ ///
+ public System.Net.Http.Headers.HttpResponseHeaders Headers { get; }
+
+ ///
+ /// The DateTime when the request was retrieved.
+ ///
+ public DateTime DownloadedAt { get; } = DateTime.UtcNow;
+
+ ///
+ /// The DateTime when the request was sent.
+ ///
+ public DateTime RequestedAt { get; }
+
+ ///
+ /// The path used when making the request.
+ ///
+ public string Path { get; }
+
+ ///
+ /// The Uri used when making the request.
+ ///
+ public Uri{{nrt?}} RequestUri { get; }
+
+ ///
+ /// The
+ ///
+ protected System.Text.Json.JsonSerializerOptions _jsonSerializerOptions;
+
+ ///
+ /// Construct the response using an HttpResponseMessage
+ ///
+ /// .
+ /// .
+ /// The raw data.
+ /// The path used when making the request.
+ /// The DateTime.UtcNow when the request was retrieved.
+ ///
+ public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, string rawContent, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions)
+ {
+ StatusCode = httpResponseMessage.StatusCode;
+ Headers = httpResponseMessage.Headers;
+ IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode;
+ ReasonPhrase = httpResponseMessage.ReasonPhrase;
+ RawContent = rawContent;
+ Path = path;
+ RequestUri = httpRequestMessage.RequestUri;
+ RequestedAt = requestedAt;
+ _jsonSerializerOptions = jsonSerializerOptions;
+ OnCreated(httpRequestMessage, httpResponseMessage);
+ }
+
+ ///
+ /// Construct the response using an HttpResponseMessage.
+ ///
+ /// .
+ /// .
+ /// The raw binary stream (only set for binary responses).
+ /// The path used when making the request.
+ /// The DateTime.UtcNow when the request was retrieved.
+ ///
+ public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, System.IO.Stream contentStream, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions)
+ {
+ StatusCode = httpResponseMessage.StatusCode;
+ Headers = httpResponseMessage.Headers;
+ IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode;
+ ReasonPhrase = httpResponseMessage.ReasonPhrase;
+ ContentStream = contentStream;
+ RawContent = string.Empty;
+ Path = path;
+ RequestUri = httpRequestMessage.RequestUri;
+ RequestedAt = requestedAt;
+ _jsonSerializerOptions = jsonSerializerOptions;
+ OnCreated(httpRequestMessage, httpResponseMessage);
+ }
+
+ partial void OnCreated(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage);
+ }
+ {{#x-http-statuses-with-return}}
+
+ ///
+ /// An interface for responses of type {{TType}}.
+ ///
+ ///
+ {{>visibility}} interface I{{.}} : IApiResponse
+ {
+ ///
+ /// Deserializes the response if the response is {{.}}.
+ ///
+ ///
+ TType {{.}}();
+
+ ///
+ /// Returns true if the response is {{.}} and the deserialized response is not null.
+ ///
+ ///
+ ///
+ bool TryDeserialize{{.}}Response({{#net60OrLater}}[NotNullWhen(true)]{{/net60OrLater}}out TType{{nrt?}} result);
+ }
+ {{/x-http-statuses-with-return}}
+}
diff --git a/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache b/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
new file mode 100644
index 000000000..789de490e
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
@@ -0,0 +1,66 @@
+{{>partial_header}}
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Microsoft.Extensions.Hosting;
+using {{packageName}}.{{clientPackage}};{{#hasImport}}
+using {{packageName}}.{{modelPackage}};{{/hasImport}}
+using {{packageName}}.Extensions;
+
+
+{{>testInstructions}}
+
+
+
+namespace {{packageName}}.Test.{{apiPackage}}
+{
+ ///
+ /// Base class for API tests
+ ///
+ public class ApiTestsBase
+ {
+ protected readonly IHost _host;
+
+ public ApiTestsBase(string[] args)
+ {
+ _host = CreateHostBuilder(args).Build();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)
+ .Configure{{apiName}}((context, services, options) =>
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#apiKeyMethods}}
+ string apiKeyTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
+ ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}(apiKeyTokenValue{{-index}}, ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(apiKeyToken{{-index}});
+
+ {{/apiKeyMethods}}
+ {{#httpBearerMethods}}
+ string bearerTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
+ BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}(bearerTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(bearerToken{{-index}});
+
+ {{/httpBearerMethods}}
+ {{#httpBasicMethods}}
+ string basicTokenUsername{{-index}} = context.Configuration[""] ?? throw new Exception("Username not found.");
+ string basicTokenPassword{{-index}} = context.Configuration[""] ?? throw new Exception("Password not found.");
+ BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}(basicTokenUsername{{-index}}, basicTokenPassword{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(basicToken{{-index}});
+
+ {{/httpBasicMethods}}
+ {{#httpSignatureMethods}}
+ HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, new List(), HashAlgorithmName.SHA256, "", 0);
+ HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(httpSignatureToken{{-index}});
+
+ {{/httpSignatureMethods}}
+ {{#oauthMethods}}
+ string oauthTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
+ OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}(oauthTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(oauthToken{{-index}});
+ {{/oauthMethods}}
+ {{/lambda.trimTrailingWithNewLine}}
+ });
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/AsModel.mustache b/templates-v7/csharp/libraries/generichost/AsModel.mustache
new file mode 100644
index 000000000..f98d3ad57
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/AsModel.mustache
@@ -0,0 +1,3 @@
+return Is{{vendorExtensions.x-http-status}}
+ ? {{#isBinary}}ContentStream{{/isBinary}}{{^isBinary}}System.Text.Json.JsonSerializer.Deserialize<{{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}>(RawContent, _jsonSerializerOptions){{/isBinary}}
+ : {{#net60OrLater}}null{{/net60OrLater}}{{^net60OrLater}}default{{/net60OrLater}};
diff --git a/templates-v7/csharp/libraries/generichost/BearerToken.mustache b/templates-v7/csharp/libraries/generichost/BearerToken.mustache
new file mode 100644
index 000000000..1dd463bcd
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/BearerToken.mustache
@@ -0,0 +1,41 @@
+\//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A token constructed from a token from a bearer token.
+ ///
+ {{>visibility}} class BearerToken : TokenBase
+ {
+ private string _raw;
+
+ ///
+ /// Constructs a BearerToken object.
+ ///
+ ///
+ ///
+ public BearerToken(string value, TimeSpan? timeout = null) : base(timeout)
+ {
+ _raw = value;
+ }
+
+ ///
+ /// Places the token in the header.
+ ///
+ ///
+ ///
+ public virtual void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string headerName)
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _raw);
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ClientUtils.mustache b/templates-v7/csharp/libraries/generichost/ClientUtils.mustache
new file mode 100644
index 000000000..c11ae8da7
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ClientUtils.mustache
@@ -0,0 +1,385 @@
+{{>partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;{{#useCompareNetObjects}}
+using KellermanSoftware.CompareNetObjects;{{/useCompareNetObjects}}
+using {{packageName}};
+using {{packageName}}.{{corePackageName}}.Options;
+{{#models}}
+{{#-first}}
+using {{packageName}}.{{modelPackage}};
+{{/-first}}
+{{/models}}
+using Models = {{packageName}}.{{modelPackage}};
+using System.Runtime.CompilerServices;
+
+namespace {{packageName}}.{{apiName}}.{{clientPackage}}
+{
+ ///
+ /// Utility functions providing some benefit to API client consumers.
+ ///
+ {{>visibility}} static {{#net70OrLater}}partial {{/net70OrLater}}class ClientUtils
+ {
+ {{#useCompareNetObjects}}
+ ///
+ /// An instance of CompareLogic.
+ ///
+ public static CompareLogic compareLogic;
+
+ ///
+ /// Static constructor to initialise compareLogic.
+ ///
+ static ClientUtils()
+ {
+ {{#equatable}}
+ ComparisonConfig comparisonConfig = new{{^net70OrLater}} ComparisonConfig{{/net70OrLater}}();
+ comparisonConfig.UseHashCodeIdentifier = true;
+ {{/equatable}}
+ compareLogic = new{{^net70OrLater}} CompareLogic{{/net70OrLater}}({{#equatable}}comparisonConfig{{/equatable}});
+ }
+ {{/useCompareNetObjects}}
+ ///
+ /// A delegate for events.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public delegate void EventHandler(object sender, T e) where T : EventArgs;
+
+ ///
+ /// Returns true when deserialization succeeds.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool TryDeserialize(string json, JsonSerializerOptions options, {{#net60OrLater}}[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] {{/net60OrLater}}out T{{#nrt}}{{#net60OrLater}}?{{/net60OrLater}}{{/nrt}} result)
+ {
+ try
+ {
+ result = JsonSerializer.Deserialize(json, options);
+ return result != null;
+ }
+ catch (Exception)
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ /// Returns true when deserialization succeeds.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool TryDeserialize(ref Utf8JsonReader reader, JsonSerializerOptions options, {{#net60OrLater}}[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] {{/net60OrLater}}out T{{#nrt}}{{#net60OrLater}}?{{/net60OrLater}}{{/nrt}} result)
+ {
+ try
+ {
+ result = JsonSerializer.Deserialize(ref reader, options);
+ return result != null;
+ }
+ catch (Exception)
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ /// The format to use for DateTime serialization.
+ ///
+ public const string ISO8601_DATETIME_FORMAT = "o";
+
+ ///
+ /// 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).
+ /// The DateTime serialization format.
+ /// Formatted string.
+ public static string{{nrt?}} ParameterToString(object{{nrt?}} obj, string{{nrt?}} format = ISO8601_DATETIME_FORMAT)
+ {
+ 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(format);
+ 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(format);
+ {{#net60OrLater}}
+ if (obj is DateOnly dateOnly)
+ return dateOnly.ToString(format);
+ {{/net60OrLater}}
+ if (obj is bool boolean)
+ return boolean ? "true" : "false";
+ {{#models}}
+ {{#model}}
+ {{#isEnum}}
+ if (obj is Models.{{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}})
+ {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}}
+ return {{classname}}ValueConverter{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}};
+ {{/isEnum}}
+ {{^isEnum}}
+ {{#vars}}
+ {{#items.isEnum}}
+ {{#items}}
+ {{^complexType}}
+ if (obj is Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}})
+ {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}}
+ return {{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}};
+ {{/complexType}}
+ {{/items}}
+ {{/items.isEnum}}
+ {{#isEnum}}
+ {{^complexType}}
+ if (obj is Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}})
+ {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}}
+ return Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}};
+ {{/complexType}}
+ {{/isEnum}}
+ {{/vars}}
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ if (obj is ICollection collection)
+ {
+ List entries = new{{^net70OrLater}} List{{/net70OrLater}}();
+ foreach (var entry in collection)
+ entries.Add(ParameterToString(entry));
+ return string.Join(",", entries);
+ }
+
+ return Convert.ToString(obj, System.Globalization.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(global::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{{nrt?}} 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{{nrt?}} 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.
+ ///
+ {{#net70OrLater}}
+ [GeneratedRegex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$")]
+ private static partial Regex JsonRegex();
+ {{/net70OrLater}}
+ {{^net70OrLater}}
+ private static readonly Regex JsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$");
+ {{/net70OrLater}}
+
+ ///
+ /// 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 {{#net70OrLater}}JsonRegex(){{/net70OrLater}}{{^net70OrLater}}JsonRegex{{/net70OrLater}}.IsMatch(mime) || mime.Equals("application/json-patch+json");
+ }
+
+ ///
+ /// Get the discriminator
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string{{nrt?}} GetDiscriminator(Utf8JsonReader utf8JsonReader, string discriminator)
+ {
+ int currentDepth = utf8JsonReader.CurrentDepth;
+
+ if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+
+ JsonTokenType startingTokenType = utf8JsonReader.TokenType;
+
+ while (utf8JsonReader.Read())
+ {
+ if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth)
+ break;
+
+ if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth)
+ break;
+
+ if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1)
+ {
+ string{{nrt?}} jsonPropertyName = utf8JsonReader.GetString();
+ utf8JsonReader.Read();
+
+ if (jsonPropertyName != null && jsonPropertyName.Equals(discriminator))
+ return utf8JsonReader.GetString();
+ }
+ }
+
+ throw new JsonException("The specified discriminator was not found.");
+ }
+
+ {{#hasApiKeyMethods}}
+ ///
+ /// An enum of headers.
+ ///
+ public enum ApiKeyHeader
+ {
+ {{#apiKeyMethods}}
+ ///
+ /// The {{keyParamName}} header.
+ ///
+ {{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}{{^-last}},{{/-last}}
+ {{/apiKeyMethods}}
+ }
+
+ ///
+ /// Converts an ApiKeyHeader to a string.
+ ///
+ ///
+ /// as a string value.
+ ///
+ {{>visibility}} static string ApiKeyHeaderToString(ApiKeyHeader value)
+ {
+ {{#net80OrLater}}
+ return value switch
+ {
+ {{#apiKeyMethods}}
+ ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}} => "{{keyParamName}}",
+ {{/apiKeyMethods}}
+ _ => throw new System.ComponentModel.InvalidEnumArgumentException(nameof(value), (int)value, typeof(ApiKeyHeader)),
+ };
+ {{/net80OrLater}}
+ {{^net80OrLater}}
+ switch(value)
+ {
+ {{#apiKeyMethods}}
+ case ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}:
+ return "{{keyParamName}}";
+ {{/apiKeyMethods}}
+ default:
+ throw new System.ComponentModel.InvalidEnumArgumentException(nameof(value), (int)value, typeof(ApiKeyHeader));
+ }
+ {{/net80OrLater}}
+ }
+
+ {{/hasApiKeyMethods}}
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/CookieContainer.mustache b/templates-v7/csharp/libraries/generichost/CookieContainer.mustache
new file mode 100644
index 000000000..f96d4fb41
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/CookieContainer.mustache
@@ -0,0 +1,22 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System.Linq;
+using System.Collections.Generic;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A class containing a CookieContainer
+ ///
+ {{>visibility}} sealed class CookieContainer
+ {
+ ///
+ /// The collection of tokens
+ ///
+ public System.Net.CookieContainer Value { get; } = new System.Net.CookieContainer();
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/DateFormats.mustache b/templates-v7/csharp/libraries/generichost/DateFormats.mustache
new file mode 100644
index 000000000..920ecda88
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateFormats.mustache
@@ -0,0 +1,2 @@
+ "yyyy'-'MM'-'dd",
+ "yyyyMMdd"
diff --git a/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache
new file mode 100644
index 000000000..1441421aa
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache
@@ -0,0 +1,52 @@
+{{>partial_header}}
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// 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
+ ///
+ {{>visibility}} class DateOnlyJsonConverter : JsonConverter
+ {
+ ///
+ /// The formats used to deserialize the date
+ ///
+ public static string[] Formats { get; } = {
+{{>DateFormats}}
+
+ };
+
+ ///
+ /// Returns a DateOnly from the Json object
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
+ if (reader.TokenType == JsonTokenType.Null)
+ throw new NotSupportedException();
+
+ string value = reader.GetString(){{nrt!}};
+
+ foreach(string format in Formats)
+ if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateOnly result))
+ return result;
+
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Writes the DateOnly to the json writer
+ ///
+ ///
+ ///
+ ///
+ public override void Write(Utf8JsonWriter writer, DateOnly dateOnlyValue, JsonSerializerOptions options) =>
+ writer.WriteStringValue(dateOnlyValue.ToString("{{{dateFormat}}}", CultureInfo.InvariantCulture));
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache
new file mode 100644
index 000000000..53fdfbee7
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache
@@ -0,0 +1,57 @@
+{{>partial_header}}
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// 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
+ ///
+ {{>visibility}} class DateOnlyNullableJsonConverter : JsonConverter
+ {
+ ///
+ /// The formats used to deserialize the date
+ ///
+ public static string[] Formats { get; } = {
+{{>DateFormats}}
+
+ };
+
+ ///
+ /// Returns a DateOnly from the Json object
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
+ if (reader.TokenType == JsonTokenType.Null)
+ return null;
+
+ string value = reader.GetString(){{nrt!}};
+
+ foreach(string format in Formats)
+ if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateOnly result))
+ return result;
+
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Writes the DateOnly to the json writer
+ ///
+ ///
+ ///
+ ///
+ public override void Write(Utf8JsonWriter writer, DateOnly? dateOnlyValue, JsonSerializerOptions options)
+ {
+ if (dateOnlyValue == null)
+ writer.WriteNullValue();
+ else
+ writer.WriteStringValue(dateOnlyValue.Value.ToString("{{{dateFormat}}}", CultureInfo.InvariantCulture));
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache b/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache
new file mode 100644
index 000000000..85ed99a2c
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache
@@ -0,0 +1,22 @@
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK",
+ "yyyy'-'MM'-'dd'T'HH':'mm':'ssK",
+ {{^supportsDateOnly}}
+ "yyyy'-'MM'-'dd",
+ {{/supportsDateOnly}}
+ "yyyyMMddTHHmmss.fffffffK",
+ "yyyyMMddTHHmmss.ffffffK",
+ "yyyyMMddTHHmmss.fffffK",
+ "yyyyMMddTHHmmss.ffffK",
+ "yyyyMMddTHHmmss.fffK",
+ "yyyyMMddTHHmmss.ffK",
+ "yyyyMMddTHHmmss.fK",
+ "yyyyMMddTHHmmssK",
+ {{^supportsDateOnly}}
+ "yyyyMMdd"
+ {{/supportsDateOnly}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache
new file mode 100644
index 000000000..bf5059fca
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache
@@ -0,0 +1,52 @@
+{{>partial_header}}
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// Formatter for {{#supportsDateOnly}}'date-time'{{/supportsDateOnly}}{{^supportsDateOnly}}'date' and 'date-time'{{/supportsDateOnly}} openapi formats ss defined by full-date - RFC3339
+ /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types
+ ///
+ {{>visibility}} class DateTimeJsonConverter : JsonConverter
+ {
+ ///
+ /// The formats used to deserialize the date
+ ///
+ public static string[] Formats { get; } = {
+{{>DateTimeFormats}}
+
+ };
+
+ ///
+ /// Returns a DateTime from the Json object
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
+ if (reader.TokenType == JsonTokenType.Null)
+ throw new NotSupportedException();
+
+ string value = reader.GetString(){{nrt!}};
+
+ foreach(string format in Formats)
+ if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result))
+ return result;
+
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// Writes the DateTime to the json writer
+ ///
+ ///
+ ///
+ ///
+ public override void Write(Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options) =>
+ writer.WriteStringValue(dateTimeValue.ToString("{{{dateTimeFormat}}}", CultureInfo.InvariantCulture));
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache
new file mode 100644
index 000000000..bbc036490
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache
@@ -0,0 +1,57 @@
+{{>partial_header}}
+using System;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// Formatter for {{#supportsDateOnly}}'date-time'{{/supportsDateOnly}}{{^supportsDateOnly}}'date' and 'date-time'{{/supportsDateOnly}} openapi formats ss defined by full-date - RFC3339
+ /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types
+ ///
+ {{>visibility}} class DateTimeNullableJsonConverter : JsonConverter
+ {
+ ///
+ /// The formats used to deserialize the date
+ ///
+ public static string[] Formats { get; } = {
+{{>DateTimeFormats}}
+
+ };
+
+ ///
+ /// Returns a DateTime from the Json object
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
+ if (reader.TokenType == JsonTokenType.Null)
+ return null;
+
+ string value = reader.GetString(){{nrt!}};
+
+ foreach(string format in Formats)
+ if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result))
+ return result;
+
+ return null;
+ }
+
+ ///
+ /// Writes the DateTime to the json writer
+ ///
+ ///
+ ///
+ ///
+ public override void Write(Utf8JsonWriter writer, DateTime? dateTimeValue, JsonSerializerOptions options)
+ {
+ if (dateTimeValue == null)
+ writer.WriteNullValue();
+ else
+ writer.WriteStringValue(dateTimeValue.Value.ToString("{{{dateTimeFormat}}}", CultureInfo.InvariantCulture));
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache b/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
new file mode 100644
index 000000000..6085b51e5
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
@@ -0,0 +1,211 @@
+{{>partial_header}}
+using System;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using {{packageName}}.{{clientPackage}};
+using {{packageName}}.{{apiPackage}};
+using {{packageName}}.Extensions;
+using Xunit;
+
+namespace {{packageName}}.Test.{{apiPackage}}
+{
+ ///
+ /// Tests the dependency injection.
+ ///
+ public class DependencyInjectionTest
+ {
+ private readonly IHost _hostUsingConfigureWithoutAClient =
+ Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) =>
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#apiKeyMethods}}
+ ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(apiKeyToken{{-index}});
+
+ {{/apiKeyMethods}}
+ {{#httpBearerMethods}}
+ BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(bearerToken{{-index}});
+
+ {{/httpBearerMethods}}
+ {{#httpBasicMethods}}
+ BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(basicToken{{-index}});
+
+ {{/httpBasicMethods}}
+ {{#httpSignatureMethods}}
+ HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
+ HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(httpSignatureToken{{-index}});
+
+ {{/httpSignatureMethods}}
+ {{#oauthMethods}}
+ OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(oauthToken{{-index}});
+
+ {{/oauthMethods}}
+ {{/lambda.trimTrailingWithNewLine}}
+ })
+ .Build();
+
+ private readonly IHost _hostUsingConfigureWithAClient =
+ Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) =>
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#apiKeyMethods}}
+ ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(apiKeyToken{{-index}});
+
+ {{/apiKeyMethods}}
+ {{#httpBearerMethods}}
+ BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(bearerToken{{-index}});
+
+ {{/httpBearerMethods}}
+ {{#httpBasicMethods}}
+ BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(basicToken{{-index}});
+
+ {{/httpBasicMethods}}
+ {{#httpSignatureMethods}}
+ HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
+ HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(httpSignatureToken{{-index}});
+
+ {{/httpSignatureMethods}}
+ {{#oauthMethods}}
+ OAuthToken oauthToken = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(oauthToken);
+
+ {{/oauthMethods}}
+ {{/lambda.trimTrailingWithNewLine}}
+ options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS));
+ })
+ .Build();
+
+ private readonly IHost _hostUsingAddWithoutAClient =
+ Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) =>
+ {
+ services.Add{{apiName}}(options =>
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#apiKeyMethods}}
+ ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(apiKeyToken{{-index}});
+
+ {{/apiKeyMethods}}
+ {{#httpBearerMethods}}
+ BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(bearerToken{{-index}});
+
+ {{/httpBearerMethods}}
+ {{#httpBasicMethods}}
+ BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(basicToken{{-index}});
+
+ {{/httpBasicMethods}}
+ {{#httpSignatureMethods}}
+ HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
+ HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(httpSignatureToken{{-index}});
+
+ {{/httpSignatureMethods}}
+ {{#oauthMethods}}
+ OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(oauthToken{{-index}});
+
+ {{/oauthMethods}}
+ {{/lambda.trimTrailingWithNewLine}}
+ });
+ })
+ .Build();
+
+ private readonly IHost _hostUsingAddWithAClient =
+ Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) =>
+ {
+ services.Add{{apiName}}(options =>
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#apiKeyMethods}}
+ ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(apiKeyToken{{-index}});
+
+ {{/apiKeyMethods}}
+ {{#httpBearerMethods}}
+ BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(bearerToken{{-index}});
+
+ {{/httpBearerMethods}}
+ {{#httpBasicMethods}}
+ BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(basicToken{{-index}});
+
+ {{/httpBasicMethods}}
+ {{#httpSignatureMethods}}
+ HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
+ HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(httpSignatureToken{{-index}});
+
+ {{/httpSignatureMethods}}
+ {{#oauthMethods}}
+ OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
+ options.AddTokens(oauthToken{{-index}});
+
+ {{/oauthMethods}}
+ {{/lambda.trimTrailingWithNewLine}}
+ options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS));
+ });
+ })
+ .Build();
+
+ ///
+ /// Test dependency injection when using the configure method
+ ///
+ [Fact]
+ public void ConfigureApiWithAClientTest()
+ {
+ {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+ Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
+
+ {{/-last}}{{/apis}}{{/apiInfo}}
+ }
+
+ ///
+ /// Test dependency injection when using the configure method
+ ///
+ [Fact]
+ public void ConfigureApiWithoutAClientTest()
+ {
+ {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+ Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
+
+ {{/-last}}{{/apis}}{{/apiInfo}}
+ }
+
+ ///
+ /// Test dependency injection when using the add method
+ ///
+ [Fact]
+ public void AddApiWithAClientTest()
+ {
+ {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+ Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
+
+ {{/-last}}{{/apis}}{{/apiInfo}}
+ }
+
+ ///
+ /// Test dependency injection when using the add method
+ ///
+ [Fact]
+ public void AddApiWithoutAClientTest()
+ {
+ {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+ Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
+
+ {{/-last}}{{/apis}}{{/apiInfo}}
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache b/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache
new file mode 100644
index 000000000..e92e67b36
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache
@@ -0,0 +1 @@
+{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}{{^isNumeric}}string{{/isNumeric}}{{/isString}}{{#isNumeric}}{{#isLong}}long{{/isLong}}{{#isFloat}}float{{/isFloat}}{{#isDouble}}double{{/isDouble}}{{#isDecimal}}decimal{{/isDecimal}}{{^isLong}}{{^isFloat}}{{^isDouble}}{{^isDecimal}}int{{/isDecimal}}{{/isDouble}}{{/isFloat}}{{/isLong}}{{/isNumeric}}{{/-first}}{{/enumVars}}{{/allowableValues}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache b/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache
new file mode 100644
index 000000000..b74fcfa0a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache
@@ -0,0 +1,24 @@
+using System;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// Useful for tracking server health
+ ///
+ {{>visibility}} class ExceptionEventArgs : EventArgs
+ {
+ ///
+ /// The ApiResponse
+ ///
+ public Exception Exception { get; }
+
+ ///
+ /// The ExceptionEventArgs
+ ///
+ ///
+ public ExceptionEventArgs(Exception exception)
+ {
+ Exception = exception;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/HmacKeyToken.mustache b/templates-v7/csharp/libraries/generichost/HmacKeyToken.mustache
new file mode 100644
index 000000000..8a24aecef
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/HmacKeyToken.mustache
@@ -0,0 +1,36 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{corePackageName}}.{{clientPackage}};
+
+namespace {{packageName}}.{{apiName}}.{{clientPackage}}
+{
+ ///
+ /// The `ADYEN_HMAC_KEY` is used to verify the incoming HMAC signature from incoming webhooks.
+ /// To protect your server from unauthorised webhook events, Adyen recommends that you use Hash-based message authentication code HMAC signatures for our webhooks.
+ /// Each webhook event will include a signature calculated using a secret `ADYEN_HMAC_KEY` key and the payload from the webhook.
+ /// By verifying this signature, you confirm that the webhook was sent by Adyen, and was not modified during transmission.
+ ///
+ {{>visibility}} class HmacKeyToken : TokenBase
+ {
+ ///
+ /// The `ADYEN_HMAC_KEY`, can be configured in .
+ ///
+ public string? AdyenHmacKey { get; }
+
+ ///
+ /// Constructs the HmacKeyToken object with the ADYEN_HMAC_KEY value provided.
+ /// This can then be accessed using .
+ ///
+ /// Your `ADYEN_HMAC_KEY`, configured in .
+ public HmacKeyToken(string hmacKey) : base()
+ {
+ AdyenHmacKey = hmacKey;
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache b/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache
new file mode 100644
index 000000000..b1387180c
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache
@@ -0,0 +1,178 @@
+{{>partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Net.Http;
+using Microsoft.Extensions.DependencyInjection;
+using {{packageName}}.{{apiPackage}};
+using {{packageName}}.{{apiName}}.{{clientPackage}};
+{{#models}}
+{{#-first}}
+using {{packageName}}.{{modelPackage}};
+{{/-first}}
+{{/models}}
+using {{packageName}}.{{corePackageName}};
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{corePackageName}}.Client;
+using {{packageName}}.{{corePackageName}}.Options;
+using {{packageName}}.{{corePackageName}}.Converters;
+
+namespace {{packageName}}.{{apiName}}.{{clientPackage}}
+{
+ ///
+ /// Provides hosting configuration for {{apiName}}
+ ///
+ {{>visibility}} class HostConfiguration
+ {
+ private readonly IServiceCollection _services;
+ private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions();
+ private readonly AdyenOptions _adyenOptions = new AdyenOptions();
+
+ ///
+ /// The base path of the API, it includes the http(s)-scheme, the host domain name, and the base path.
+ /// This value can change when `ConfigureAdyenOptions` is called in ). The new value will be based on the ..
+ ///
+ public static string BASE_URL = "{{{basePath}}}";
+
+ ///
+ /// Instantiates the HostConfiguration (custom JsonConverters, Events, HttpClient) with the necessary dependencies to communicate with the API.
+ ///
+ ///
+ public HostConfiguration(IServiceCollection services)
+ {
+ _services = services;
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(JsonStringEnumConverter)) == null)
+ _jsonOptions.Converters.Add(new JsonStringEnumConverter());
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateTimeJsonConverter)) == null)
+ _jsonOptions.Converters.Add(new DateTimeJsonConverter());
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateTimeNullableJsonConverter)) == null)
+ _jsonOptions.Converters.Add(new DateTimeNullableJsonConverter());
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(ByteArrayConverter)) == null)
+ _jsonOptions.Converters.Add(new ByteArrayConverter());
+ {{#supportsDateOnly}}
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateOnlyJsonConverter)) == null)
+ _jsonOptions.Converters.Add(new DateOnlyJsonConverter());
+ if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateOnlyNullableJsonConverter)) == null)
+ _jsonOptions.Converters.Add(new DateOnlyNullableJsonConverter());
+ {{/supportsDateOnly}}
+ {{#models}}
+ {{#model}}
+ {{#isEnum}}
+ _jsonOptions.Converters.Add(new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}JsonConverter());
+ _jsonOptions.Converters.Add(new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}NullableJsonConverter());
+ {{/isEnum}}
+ {{^isEnum}}
+ _jsonOptions.Converters.Add(new {{classname}}JsonConverter());
+ {{/isEnum}}
+ {{/model}}
+ {{/models}}
+ JsonSerializerOptionsProvider jsonSerializerOptionsProvider = new{{^net60OrLater}} JsonSerializerOptionsProvider{{/net60OrLater}}(_jsonOptions);
+ _services.AddSingleton(jsonSerializerOptionsProvider);
+ {{#useSourceGeneration}}
+
+ {{#models}}
+ {{#-first}}
+ _jsonOptions.TypeInfoResolver = System.Text.Json.Serialization.Metadata.JsonTypeInfoResolver.Combine(
+ {{/-first}}
+ {{/models}}
+ {{#lambda.joinLinesWithComma}}
+ {{#models}}
+ {{#model}}
+ new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}SerializationContext(){{#-last}},{{/-last}}
+ {{/model}}
+ {{/models}}
+ {{/lambda.joinLinesWithComma}}
+ {{#models}}
+ {{#-last}}
+
+ new System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver()
+ );
+ {{/-last}}
+ {{/models}}
+
+ {{/useSourceGeneration}}
+ _services.AddSingleton<{{interfacePrefix}}ApiFactory, ApiFactory>();{{#apiInfo}}{{#apis}}
+ _services.AddSingleton<{{classname}}Events>();{{/apis}}{{/apiInfo}}
+ }
+
+ ///
+ /// Configures the and .
+ ///
+ /// Configures the .
+ /// Configures the .
+ /// .
+ public HostConfiguration Add{{apiName}}HttpClients(Action{{nrt?}} httpClientOptions = null, Action{{nrt?}} httpClientBuilderOptions = null)
+ {
+ Action httpClientAction = httpClient =>
+ {
+ // Configure HttpClient set by the user.
+ httpClientOptions?.Invoke(httpClient);
+
+ // Set BaseAddress if it's not set.
+ if (httpClient.BaseAddress == null)
+ httpClient.BaseAddress = new Uri(UrlBuilderExtensions.ConstructHostUrl(_adyenOptions, BASE_URL));
+ };
+
+ List builders = new List();
+
+ {{#models}}
+ {{#model.vendorExtensions.x-has-at-least-one-webhook-root}}
+ _services.AddSingleton<{{packageName}}.{{apiName}}.Handlers.{{interfacePrefix}}{{apiName}}Handler, {{packageName}}.{{apiName}}.Handlers.{{apiName}}Handler>();
+ {{/model.vendorExtensions.x-has-at-least-one-webhook-root}}
+ {{/models}}
+ {{#apiInfo}}{{#apis}}builders.Add(_services.AddHttpClient<{{interfacePrefix}}{{classname}}, {{classname}}>(httpClientAction));
+ {{/apis}}{{/apiInfo}}
+ if (httpClientBuilderOptions != null)
+ foreach (IHttpClientBuilder builder in builders)
+ httpClientBuilderOptions(builder);
+
+ return this;
+ }
+
+ ///
+ /// Configures the .
+ ///
+ /// Configures the .
+ /// .
+ public HostConfiguration ConfigureJsonOptions(Action jsonSerializerOptions)
+ {
+ jsonSerializerOptions(_jsonOptions);
+
+ return this;
+ }
+
+ ///
+ /// Configures the (e.g. Environment, LiveEndpointPrefix).
+ ///
+ /// Configures the .
+ /// .
+ public HostConfiguration ConfigureAdyenOptions(Action adyenOptions)
+ {
+ adyenOptions(_adyenOptions);
+ {{#hasApiKeyMethods}}
+ _services.AddSingleton>(
+ new TokenProvider(
+ new ApiKeyToken(_adyenOptions.AdyenApiKey, ClientUtils.ApiKeyHeader.X_API_Key, "")
+ )
+ );
+ {{/hasApiKeyMethods}}
+
+ {{#models}}
+ {{#model.vendorExtensions.x-has-at-least-one-webhook-root}}
+ _services.AddSingleton>(
+ new TokenProvider(
+ new HmacKeyToken(_adyenOptions.AdyenHmacKey)
+ )
+ );
+ {{/model.vendorExtensions.x-has-at-least-one-webhook-root}}
+ {{/models}}
+ return this;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
new file mode 100644
index 000000000..8b69a8c0b
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
@@ -0,0 +1,679 @@
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+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}}.{{clientPackage}}
+{
+ ///
+ /// Class for HttpSigning auth related parameter and methods
+ ///
+ {{>visibility}} class HttpSigningConfiguration
+ {
+ ///
+ /// Create an instance
+ ///
+ public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{nrt?}} keyPassPhrase, List httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod)
+ {
+ KeyId = keyId;
+ KeyFilePath = keyFilePath;
+ KeyPassPhrase = keyPassPhrase;
+ HttpSigningHeader = httpSigningHeader;
+ HashAlgorithm = hashAlgorithm;
+ SigningAlgorithm = signingAlgorithm;
+ SignatureValidityPeriod = signatureValidityPeriod;
+ }
+
+ ///
+ ///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{{nrt?}} 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; } = HashAlgorithmName.SHA256;
+
+ ///
+ /// Gets the signing algorithm
+ ///
+ public string SigningAlgorithm { get; set; }
+
+ ///
+ /// Gets the Signature validity period in seconds
+ ///
+ public int SignatureValidityPeriod { get; set; }
+
+ private enum PrivateKeyType
+ {
+ None = 0,
+ RSA = 1,
+ ECDSA = 2,
+ }
+
+ ///
+ /// Gets the Headers for HttpSigning
+ ///
+ ///
+ ///
+ ///
+ internal Dictionary GetHttpSignedHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, System.Threading.CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
+ {
+ if (request.RequestUri == null)
+ throw new NullReferenceException("The request URI was null");
+
+ 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)");
+
+ var dateTime = DateTime.Now;
+ string digest = String.Empty;
+
+ if (HashAlgorithm == HashAlgorithmName.SHA256)
+ {
+ var bodyDigest = GetStringHash(HashAlgorithm, requestBody);
+ digest = string.Format("SHA-256={0}", Convert.ToBase64String(bodyDigest));
+ }
+ else if (HashAlgorithm == HashAlgorithmName.SHA512)
+ {
+ var bodyDigest = GetStringHash(HashAlgorithm, 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))
+ httpSignatureHeader.Add(header.ToLower(), request.RequestUri.ToString());
+ 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(), request.RequestUri.ToString());
+ HttpSignedRequestHeader.Add(HEADER_HOST, request.RequestUri.ToString());
+ }
+ 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 request.Headers)
+ {
+ 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));
+
+ //Concatenate headers value separated by new line
+ var headerValuesString = string.Join("\n", headerValuesList);
+ var signatureStringHash = GetStringHash(HashAlgorithm, headerValuesString);
+ string{{nrt?}} 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(HashAlgorithmName hashAlgorithmName, string stringToBeHashed)
+ {
+ HashAlgorithm{{nrt?}} hashAlgorithm = null;
+
+ if (hashAlgorithmName == HashAlgorithmName.SHA1)
+ hashAlgorithm = SHA1.Create();
+
+ if (hashAlgorithmName == HashAlgorithmName.SHA256)
+ hashAlgorithm = SHA256.Create();
+
+ if (hashAlgorithmName == HashAlgorithmName.SHA512)
+ hashAlgorithm = SHA512.Create();
+
+ if (hashAlgorithmName == HashAlgorithmName.MD5)
+ hashAlgorithm = MD5.Create();
+
+ if (hashAlgorithm == null)
+ throw new NullReferenceException($"{ nameof(hashAlgorithm) } was null.");
+
+ byte[] bytes = Encoding.UTF8.GetBytes(stringToBeHashed);
+ byte[] 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)
+ {
+ if (KeyPassPhrase == null)
+ throw new NullReferenceException($"{ nameof(KeyPassPhrase) } was null.");
+
+ RSA{{nrt?}} rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase);
+
+ if (rsa == null)
+ return string.Empty;
+ else 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)
+ {
+ {{#net60OrLater}}
+ 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();
+
+ var byteCount = 0;
+ if (KeyPassPhrase != null)
+ {
+ IntPtr unmanagedString = IntPtr.Zero;
+ try
+ {
+ // convert secure string to byte array
+ unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(KeyPassPhrase);
+
+ string ptrToStringUni = Marshal.PtrToStringUni(unmanagedString) ?? throw new NullReferenceException();
+
+ ecdsa.ImportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(ptrToStringUni), 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;
+ {{/net60OrLater}}
+ {{^net60OrLater}}
+ throw new Exception("ECDSA signing is supported only on NETCOREAPP3_0 and above");
+ {{/net60OrLater}}
+ }
+
+ private byte[] ConvertToECDSAANS1Format(byte[] signedBytes)
+ {
+ var derBytes = new List();
+ byte derLength = 68; //default length for ECDSA code signing 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 length, 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{{nrt?}} GetRSAProviderFromPemFile(String pemfile, SecureString{{nrt?}} keyPassPhrase = null)
+ {
+ const String pempubheader = "-----BEGIN PUBLIC KEY-----";
+ const String pempubfooter = "-----END PUBLIC KEY-----";
+ bool isPrivateKeyFile = true;
+ byte[]{{nrt?}} 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, keyPassPhrase);
+
+ if (pemkey == null)
+ return null;
+
+ return DecodeRSAPrivateKey(pemkey);
+ }
+ return null;
+ }
+
+ private byte[]{{nrt?}} ConvertPrivateKeyToBytes(String instr, SecureString{{nrt?}} keyPassPhrase = 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 (global::System.FormatException)
+ {
+ StringReader str = new StringReader(pvkstr);
+
+ //-------- read PEM encryption info. lines and extract salt -----
+ if (!str.ReadLine(){{nrt!}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null?
+ return null;
+
+ String saltline = str.ReadLine(){{nrt!}}; // TODO: what do we do here if ReadLine is null?
+ 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 (global::System.FormatException)
+ { //data is not in base64 format
+ return null;
+ }
+
+ // TODO: what do we do here if keyPassPhrase is null?
+ byte[] deskey = GetEncryptedKey(salt, keyPassPhrase{{nrt!}}, 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[]{{nrt?}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
+
+ return rsakey;
+ }
+ }
+
+ private RSACryptoServiceProvider{{nrt?}} 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 concatenated Mi hashed results
+
+ byte[] psbytes = new byte[secpswd.Length];
+ unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
+ Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
+ Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
+
+ // --- concatenate 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 concatenate results D1, D2 ... into keymaterial bytes ----
+ MD5 md5 = MD5.Create();
+ byte[]{{nrt?}} 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{{nrt!}}, hashtarget, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
+ 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); //concatenate 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{{nrt!}}, 0, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
+ Array.Clear(hashtarget, 0, hashtarget.Length);
+ Array.Clear(keymaterial, 0, keymaterial.Length);
+ return deskey;
+ }
+
+ private byte[]{{nrt?}} 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;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
new file mode 100644
index 000000000..881682e89
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
@@ -0,0 +1,45 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A token constructed from an HttpSigningConfiguration
+ ///
+ {{>visibility}} class HttpSignatureToken : TokenBase
+ {
+ private HttpSigningConfiguration _configuration;
+
+ ///
+ /// Constructs an HttpSignatureToken object.
+ ///
+ ///
+ ///
+ public HttpSignatureToken(HttpSigningConfiguration configuration, TimeSpan? timeout = null) : base(timeout)
+ {
+ _configuration = configuration;
+ }
+
+ ///
+ /// Places the token in the header.
+ ///
+ ///
+ ///
+ ///
+ public void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
+ {
+ var signedHeaders = _configuration.GetHttpSignedHeader(request, requestBody, cancellationToken);
+
+ foreach (var signedHeader in signedHeaders)
+ request.Headers.Add(signedHeader.Key, signedHeader.Value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/IApi.mustache b/templates-v7/csharp/libraries/generichost/IApi.mustache
new file mode 100644
index 000000000..a2fc6d1ea
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/IApi.mustache
@@ -0,0 +1,13 @@
+namespace {{packageName}}.{{apiPackage}}
+{
+ ///
+ /// Interface for interacting with any {{packageName}} API using .
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{packageName}}ApiService
+ {
+ ///
+ /// The object, best practice: instantiate and manage object using the .
+ ///
+ System.Net.Http.HttpClient HttpClient { get; }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache b/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache
new file mode 100644
index 000000000..b3df192b6
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache
@@ -0,0 +1,41 @@
+{{>partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using {{packageName}}.{{apiName}};
+using {{packageName}}.{{apiName}}.{{clientPackage}};
+
+namespace {{packageName}}.{{apiName}}.Extensions
+{
+ ///
+ /// Extension methods for IHostBuilder.
+ ///
+ {{>visibility}} static class HostBuilderExtensions
+ {
+ ///
+ /// Add the {{apiName}} API services to the .
+ /// You can optionally configure the and .
+ ///
+ /// .
+ /// Configures the , , and .
+ /// Configures the .
+ /// Configures the .
+ public static IHostBuilder Configure{{apiName}}(this IHostBuilder hostBuilder, Action hostConfigurationOptions, Action? httpClientOptions = null, Action? httpClientBuilderOptions = null)
+ {
+ hostBuilder.ConfigureServices((context, services) =>
+ {
+ HostConfiguration hostConfiguration = new HostConfiguration(services);
+
+ hostConfigurationOptions(context, services, hostConfiguration);
+
+ hostConfiguration.Add{{apiName}}HttpClients(httpClientOptions, httpClientBuilderOptions);
+ });
+
+ return hostBuilder;
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache b/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache
new file mode 100644
index 000000000..053c0226a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache
@@ -0,0 +1,75 @@
+{{>partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Net.Http;
+using Microsoft.Extensions.DependencyInjection;{{#supportsRetry}}
+using Polly.Timeout;
+using Polly.Extensions.Http;
+using Polly;{{/supportsRetry}}
+
+namespace {{packageName}}.Extensions
+{
+ ///
+ /// Extension methods for IHttpClientBuilder
+ ///
+ {{>visibility}} static class IHttpClientBuilderExtensions
+ {
+ {{#supportsRetry}}
+ ///
+ /// Adds a Polly retry policy to your clients.
+ ///
+ ///
+ ///
+ ///
+ public static IHttpClientBuilder AddRetryPolicy(this IHttpClientBuilder client, int retries)
+ {
+ client.AddPolicyHandler(RetryPolicy(retries));
+
+ return client;
+ }
+
+ ///
+ /// Adds a Polly timeout policy to your clients.
+ ///
+ ///
+ ///
+ ///
+ public static IHttpClientBuilder AddTimeoutPolicy(this IHttpClientBuilder client, TimeSpan timeout)
+ {
+ client.AddPolicyHandler(TimeoutPolicy(timeout));
+
+ return client;
+ }
+
+ ///
+ /// Adds a Polly circuit breaker to your clients.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IHttpClientBuilder AddCircuitBreakerPolicy(this IHttpClientBuilder client, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak)
+ {
+ client.AddTransientHttpErrorPolicy(builder => CircuitBreakerPolicy(builder, handledEventsAllowedBeforeBreaking, durationOfBreak));
+
+ return client;
+ }
+
+ private static Polly.Retry.AsyncRetryPolicy RetryPolicy(int retries)
+ => HttpPolicyExtensions
+ .HandleTransientHttpError()
+ .Or()
+ .RetryAsync(retries);
+
+ private static AsyncTimeoutPolicy TimeoutPolicy(TimeSpan timeout)
+ => Policy.TimeoutAsync(timeout);
+
+ private static Polly.CircuitBreaker.AsyncCircuitBreakerPolicy CircuitBreakerPolicy(
+ PolicyBuilder builder, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak)
+ => builder.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking, durationOfBreak);
+ {{/supportsRetry}}
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache b/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache
new file mode 100644
index 000000000..fb32d770a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache
@@ -0,0 +1,33 @@
+{{>partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{apiName}}.{{clientPackage}};
+
+namespace {{packageName}}.{{apiName}}.Extensions
+{
+ ///
+ /// Extension methods for .
+ ///
+ {{>visibility}} static class ServiceCollectionExtensions
+ {
+ ///
+ /// Add the {{packageName}} {{apiName}} API services to your .
+ ///
+ /// .
+ /// Configures the .
+ /// Configures the .
+ /// Configures the .
+ public static void Add{{apiName}}Services(this IServiceCollection services, Action hostConfigurationOptions, Action? httpClientOptions = null, Action? httpClientBuilderOptions = null)
+ {
+ HostConfiguration hostConfiguration = new{{^net70OrLater}} HostConfiguration{{/net70OrLater}}(services);
+ hostConfigurationOptions(hostConfiguration);
+ hostConfiguration.Add{{apiName}}HttpClients(httpClientOptions, httpClientBuilderOptions);
+ }
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache b/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache
new file mode 100644
index 000000000..dd576dd0f
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache
@@ -0,0 +1 @@
+{{#equatable}}{{#readOnlyVars}}{{#-first}}IEquatable<{{classname}}{{nrt?}}> {{/-first}}{{/readOnlyVars}}{{/equatable}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache b/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache
new file mode 100644
index 000000000..7c3f0e02a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache
@@ -0,0 +1 @@
+{{#validatable}}IValidatableObject {{/validatable}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/JsonConverter.mustache b/templates-v7/csharp/libraries/generichost/JsonConverter.mustache
new file mode 100644
index 000000000..391e00e66
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/JsonConverter.mustache
@@ -0,0 +1,661 @@
+ ///
+ /// A Json converter for type
+ ///
+ {{>visibility}} class {{classname}}JsonConverter : JsonConverter<{{classname}}>
+ {
+ {{#allVars}}
+ {{#isDateTime}}
+ ///
+ /// The format to use to serialize {{name}}.
+ ///
+ public static string {{name}}Format { get; set; } = "{{{dateTimeFormat}}}";
+
+ {{/isDateTime}}
+ {{#isDate}}
+ ///
+ /// The format to use to serialize {{name}}.
+ ///
+ public static string {{name}}Format { get; set; } = "{{{dateFormat}}}";
+
+ {{/isDate}}
+ {{/allVars}}
+ ///
+ /// Deserializes json to .
+ ///
+ /// .
+ /// .
+ /// The , initialized from .
+ /// .
+ ///
+ public override {{classname}} Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert, JsonSerializerOptions jsonSerializerOptions)
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.trimLineBreaks}}
+ int currentDepth = utf8JsonReader.CurrentDepth;
+
+ if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray)
+ throw new JsonException();
+
+ JsonTokenType startingTokenType = utf8JsonReader.TokenType;
+
+ {{#allVars}}
+ Option<{{#isInnerEnum}}{{^isMap}}{{classname}}.{{/isMap}}{{/isInnerEnum}}{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}> {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default;
+ {{#-last}}
+
+ {{/-last}}
+ {{/allVars}}
+ {{#discriminator}}
+ {{#children}}
+ {{#-first}}
+ string{{nrt?}} discriminator = ClientUtils.GetDiscriminator(utf8JsonReader, "{{discriminator.propertyBaseName}}");
+
+ {{/-first}}
+ if (discriminator != null && discriminator.Equals("{{name}}"))
+ return JsonSerializer.Deserialize<{{{classname}}}>(ref utf8JsonReader, jsonSerializerOptions) ?? throw new JsonException("The result was an unexpected value.");
+
+ {{/children}}
+ {{/discriminator}}
+ {{#model.discriminator}}
+ {{#model.hasDiscriminatorWithNonEmptyMapping}}
+ {{#mappedModels}}
+ {{#model}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ {{classname}}{{nrt?}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}} = null;
+ {{#-last}}
+
+ {{/-last}}
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/model}}
+ {{/mappedModels}}
+ Utf8JsonReader utf8JsonReaderDiscriminator = utf8JsonReader;
+ while (utf8JsonReaderDiscriminator.Read())
+ {
+ if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderDiscriminator.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth)
+ break;
+
+ if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderDiscriminator.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth)
+ break;
+
+ if (utf8JsonReaderDiscriminator.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth - 1)
+ {
+ string{{nrt?}} jsonPropertyName = utf8JsonReaderDiscriminator.GetString();
+ utf8JsonReaderDiscriminator.Read();
+ if (jsonPropertyName{{nrt?}}.Equals("{{propertyBaseName}}"){{#nrt}} ?? false{{/nrt}})
+ {
+ string{{nrt?}} discriminator = utf8JsonReaderDiscriminator.GetString();
+ {{#mappedModels}}
+ if (discriminator{{nrt?}}.Equals("{{mappingName}}"){{#nrt}} ?? false{{/nrt}})
+ {
+ Utf8JsonReader utf8JsonReader{{model.classname}} = utf8JsonReader;
+ {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}} = JsonSerializer.Deserialize<{{{model.classname}}}>(ref utf8JsonReader{{model.classname}}, jsonSerializerOptions);
+ }
+ {{/mappedModels}}
+ }
+ }
+ }
+
+ {{/model.hasDiscriminatorWithNonEmptyMapping}}
+ {{/model.discriminator}}
+ {{^model.discriminator}}
+ {{#composedSchemas}}
+ {{#oneOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ {{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default;
+ {{#-last}}
+
+ Utf8JsonReader utf8JsonReaderOneOf = utf8JsonReader;
+ while (utf8JsonReaderOneOf.Read())
+ {
+ if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderOneOf.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderOneOf.CurrentDepth)
+ break;
+
+ if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderOneOf.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderOneOf.CurrentDepth)
+ break;
+
+ if (utf8JsonReaderOneOf.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderOneOf.CurrentDepth - 1)
+ {
+ {{#oneOf}}
+ Utf8JsonReader utf8JsonReader{{name}} = utf8JsonReader;
+ ClientUtils.TryDeserialize<{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>(ref utf8JsonReader{{name}}, jsonSerializerOptions, out {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}});
+ {{^-last}}
+
+ {{/-last}}
+ {{/oneOf}}
+ }
+ }
+ {{/-last}}
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/oneOf}}
+
+ {{#anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ {{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default;
+ {{#-last}}
+
+ Utf8JsonReader utf8JsonReaderAnyOf = utf8JsonReader;
+ while (utf8JsonReaderAnyOf.Read())
+ {
+ if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderAnyOf.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderAnyOf.CurrentDepth)
+ break;
+
+ if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderAnyOf.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderAnyOf.CurrentDepth)
+ break;
+
+ if (utf8JsonReaderAnyOf.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderAnyOf.CurrentDepth - 1)
+ {
+ {{#anyOf}}
+ Utf8JsonReader utf8JsonReader{{name}} = utf8JsonReader;
+ ClientUtils.TryDeserialize<{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>(ref utf8JsonReader{{name}}, jsonSerializerOptions, out {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}});
+ {{^-last}}
+
+ {{/-last}}
+ {{/anyOf}}
+ }
+ }
+ {{/-last}}
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/anyOf}}
+
+ {{/composedSchemas}}
+ {{/model.discriminator}}
+ while (utf8JsonReader.Read())
+ {
+ if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth)
+ break;
+
+ if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth)
+ break;
+
+ if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1)
+ {
+ string{{nrt?}} jsonPropertyName = utf8JsonReader.GetString();
+ utf8JsonReader.Read();
+
+ switch (jsonPropertyName)
+ {
+ {{#allVars}}
+ case "{{baseName}}":
+ {{#isString}}
+ {{^isMap}}
+ {{^isEnum}}
+ {{^isUuid}}
+ {{#isDecimal}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.GetDecimal());
+ {{/isDecimal}}
+ {{^isDecimal}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.GetString(){{^isNullable}}{{nrt!}}{{/isNullable}});
+ {{/isDecimal}}
+ {{/isUuid}}
+ {{/isEnum}}
+ {{/isMap}}
+ {{/isString}}
+ {{#isBoolean}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (bool?)null : utf8JsonReader.GetBoolean());
+ {{/isBoolean}}
+ {{#isNumeric}}
+ {{^isEnum}}
+ {{#isDouble}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (double?)null : utf8JsonReader.GetDouble());
+ {{/isDouble}}
+ {{#isDecimal}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (decimal?)null : utf8JsonReader.GetDecimal());
+ {{/isDecimal}}
+ {{#isFloat}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (float?)null : (float)utf8JsonReader.GetDouble());
+ {{/isFloat}}
+ {{#isLong}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#vendorExtensions.x-unsigned}}u{{/vendorExtensions.x-unsigned}}long?)null : utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int64());
+ {{/isLong}}
+ {{^isLong}}
+ {{^isFloat}}
+ {{^isDecimal}}
+ {{^isDouble}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#vendorExtensions.x-unsigned}}u{{/vendorExtensions.x-unsigned}}int?)null : utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int32());
+ {{/isDouble}}
+ {{/isDecimal}}
+ {{/isFloat}}
+ {{/isLong}}
+ {{/isEnum}}
+ {{/isNumeric}}
+ {{#isDate}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize<{{#supportsDateOnly}}DateOnly{{/supportsDateOnly}}{{^supportsDateOnly}}DateTime{{/supportsDateOnly}}{{#isNullable}}?{{/isNullable}}>(ref utf8JsonReader, jsonSerializerOptions));
+ {{/isDate}}
+ {{#isDateTime}}
+ {{! Fix: Added support for DateTimeOffset, when `useDateTimeOffset=true` }}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize(ref utf8JsonReader, jsonSerializerOptions));
+ {{/isDateTime}}
+ {{#isEnum}}
+ {{^isMap}}
+ {{#isNumeric}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}?)null : ({{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}})utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int32());
+ {{/isNumeric}}
+ {{^isNumeric}}
+ string{{nrt?}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = utf8JsonReader.GetString();
+ {{^isInnerEnum}}
+ if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue != null)
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}{{{datatypeWithEnum}}}ValueConverter.FromStringOrDefault({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue));
+ {{/isInnerEnum}}
+ {{#isInnerEnum}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}{{classname}}.{{{datatypeWithEnum}}}.FromStringOrDefault({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue));
+ {{/isInnerEnum}}
+ {{/isNumeric}}
+ {{/isMap}}
+ {{/isEnum}}
+ {{#isUuid}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (Guid?)null : utf8JsonReader.GetGuid());
+ {{/isUuid}}
+ {{^isUuid}}
+ {{^isEnum}}
+ {{^isString}}
+ {{^isBoolean}}
+ {{^isNumeric}}
+ {{^isDate}}
+ {{^isDateTime}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize<{{{datatypeWithEnum}}}>(ref utf8JsonReader, jsonSerializerOptions){{^isNullable}}{{nrt!}}{{/isNullable}});
+ {{/isDateTime}}
+ {{/isDate}}
+ {{/isNumeric}}
+ {{/isBoolean}}
+ {{/isString}}
+ {{/isEnum}}
+ {{/isUuid}}
+ break;
+ {{/allVars}}
+ default:
+ break;
+ }
+ }
+ }
+
+ {{! Required fields }}
+ {{#allVars}}
+ {{#required}}
+ if (!{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.IsSet)
+ throw new ArgumentException("Property is required for class {{classname}}.", nameof({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}));
+
+ {{/required}}
+ {{/allVars}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ {{#model.discriminator}}
+ {{#model.hasDiscriminatorWithNonEmptyMapping}}
+ {{^model.composedSchemas.anyOf}}
+ {{#mappedModels}}
+ if ({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}} != null)
+ return new {{classname}}({{#lambda.joinWithComma}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}});
+
+ {{#-last}}
+ throw new JsonException();
+ {{/-last}}
+ {{/mappedModels}}
+ {{/model.composedSchemas.anyOf}}
+ {{/model.hasDiscriminatorWithNonEmptyMapping}}
+ {{/model.discriminator}}
+ {{^composedSchemas.oneOf}}
+ {{^required}}
+ {{#model.composedSchemas.anyOf}}
+ Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} == null
+ ? default
+ : new Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}});
+ {{/model.composedSchemas.anyOf}}
+ {{#-last}}
+
+ {{/-last}}
+ {{/required}}
+ return new {{classname}}({{#lambda.joinWithComma}}{{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue{{#required}}.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/required}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}});
+ {{/composedSchemas.oneOf}}
+ {{^model.discriminator}}
+ {{#composedSchemas}}
+ {{#oneOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}?.Type != null)
+ return new {{classname}}({{#lambda.joinWithComma}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}} {{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}ParsedValue{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}});
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{#-last}}
+ throw new JsonException();
+ {{/-last}}
+ {{/oneOf}}
+ {{/composedSchemas}}
+ {{/model.discriminator}}
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/lambda.trimLineBreaks}}
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ ///
+ /// Serializes a .
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override void Write(Utf8JsonWriter writer, {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions jsonSerializerOptions)
+ {
+ {{#lambda.trimLineBreaks}}
+ {{#lambda.copyText}}
+ {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}
+ {{/lambda.copyText}}
+ {{#discriminator}}
+ {{#children}}
+ if ({{#lambda.paste}}{{/lambda.paste}} is {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}){
+ JsonSerializer.Serialize<{{{classname}}}>(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, jsonSerializerOptions);
+ return;
+ }
+
+ {{/children}}
+ {{/discriminator}}
+ {{! start | support oneOf without discriminator.mapping property - WriteProperties }}
+ {{^model.discriminator}}
+ {{#composedSchemas.oneOf}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ {{/composedSchemas.oneOf}}
+ {{/model.discriminator}}
+ {{! end | oneOf support without discriminator.mapping property - WriteProperties }}
+ {{! start | support oneOf without discriminator.mapping property- WriteStartObject }}
+ {{^model.discriminator}}{{#oneOf}}{{#-first}}/* {{/-first}}{{/oneOf}}{{/model.discriminator}}
+ writer.WriteStartObject();
+ {{^model.discriminator}}{{#oneOf}}{{#-first}} */{{/-first}}{{/oneOf}}{{/model.discriminator}}
+ {{! end | support oneOf without discriminator.mapping property - WriteStartObject }}
+ {{#model.discriminator}}
+ {{#model.hasDiscriminatorWithNonEmptyMapping}}
+ {{#composedSchemas.oneOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ {{#isPrimitiveType}}
+ {{#isString}}
+ writer.WriteString("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value);
+ {{/isString}}
+ {{#isBoolean}}
+ writer.WriteBoolean("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value);
+ {{/isBoolean}}
+ {{#isNumeric}}
+ writer.WriteNumber("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value);
+ {{/isNumeric}}
+ {{/isPrimitiveType}}
+ {{^isPrimitiveType}}
+ {
+ {{baseType}}JsonConverter {{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}} = ({{baseType}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.GetType()));
+ {{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}}.WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ }
+ {{/isPrimitiveType}}
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.oneOf}}
+ {{/model.hasDiscriminatorWithNonEmptyMapping}}
+ {{/model.discriminator}}
+ {{^model.discriminator}}
+ {{#composedSchemas}}
+ {{#anyOf}}
+ if ({{#lambda.joinWithAmpersand}}{{^required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.IsSet {{/required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}} != null{{/lambda.joinWithAmpersand}})
+ {{#isPrimitiveType}}
+ {{#isString}}
+ writer.WriteString("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value);
+ {{/isString}}
+ {{#isBoolean}}
+ writer.WriteBoolean("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value);
+ {{/isBoolean}}
+ {{#isNumeric}}
+ writer.WriteNumber("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value);
+ {{/isNumeric}}
+ {{#isEnum}}
+ {
+ {{datatypeWithEnum}}JsonConverter {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}.GetType()));
+ {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter.Write{{^isEnumRef}}Properties{{/isEnumRef}}(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}, jsonSerializerOptions);
+ }
+ {{/isEnum}}
+ {{/isPrimitiveType}}
+ {{^isPrimitiveType}}
+ {
+ {{datatypeWithEnum}}JsonConverter {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}.GetType()));
+ {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter.Write{{^isEnumRef}}Properties{{/isEnumRef}}(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}, jsonSerializerOptions);
+ }
+ {{/isPrimitiveType}}
+
+ {{/anyOf}}
+ {{/composedSchemas}}
+ {{/model.discriminator}}
+ WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, jsonSerializerOptions);
+ {{! start | support oneOf without discriminator.mapping property- WriteEndObject }}
+ {{^model.discriminator}}{{#oneOf}}{{#-first}}/* {{/-first}}{{/oneOf}}{{/model.discriminator}}
+ writer.WriteEndObject();
+ {{^model.discriminator}}{{#oneOf}}{{#-first}} */{{/-first}}{{/oneOf}}{{/model.discriminator}}
+ {{! end | support oneOf without discriminator.mapping property - WriteEndObject }}
+ {{/lambda.trimLineBreaks}}
+ }
+
+ ///
+ /// Serializes the properties of .
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void WriteProperties(Utf8JsonWriter writer, {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions jsonSerializerOptions)
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.trimLineBreaks}}
+
+ {{#allVars}}
+ {{#isDiscriminator}}
+ {{^model.composedSchemas.anyOf}}
+ {{^model.composedSchemas.oneOf}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ writer.WriteString("{{baseName}}", {{^isEnum}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{/isEnum}}{{#isEnum}}{{#isInnerEnum}}{{classname}}.{{{datatypeWithEnum}}}.ToJsonValue{{/isInnerEnum}}{{^isInnerEnum}}{{{datatypeWithEnum}}}ValueConverter.ToJsonValue{{/isInnerEnum}}({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}{{/required}}){{/isEnum}});
+
+ {{/model.composedSchemas.oneOf}}
+ {{/model.composedSchemas.anyOf}}
+ {{/isDiscriminator}}
+ {{^isDiscriminator}}
+ {{#isString}}
+ {{^isMap}}
+ {{^isEnum}}
+ {{^isUuid}}
+ {{#lambda.copyText}}
+ {{#isDecimal}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.ToString());
+ {{/isDecimal}}
+ {{^isDecimal}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}});
+ {{/isDecimal}}
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isUuid}}
+ {{/isEnum}}
+ {{/isMap}}
+ {{/isString}}
+ {{#isBoolean}}
+ {{#lambda.copyText}}
+ writer.WriteBoolean("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}});
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isBoolean}}
+ {{^isEnum}}
+ {{#isNumeric}}
+ {{#lambda.copyText}}
+ writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}});
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isNumeric}}
+ {{/isEnum}}
+ {{#isDate}}
+ {{#lambda.copyText}}
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}.ToString({{name}}Format));
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isDate}}
+ {{#isDateTime}}
+ {{#lambda.copyText}}
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}.ToString({{name}}Format));
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isDateTime}}
+ {{#isEnum}}
+ {{#isNumeric}}
+ {{#lambda.copyText}}
+ writer.WriteNumber("{{baseName}}", {{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}));
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isNumeric}}
+ {{^isMap}}
+ {{^isNumeric}}
+ {{#isInnerEnum}}
+ {{#isNullable}}
+ var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{classname}}.{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}{{nrt!}}.Value{{/isNullable}}{{/required}});
+ if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue != null)
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue);
+ else
+ writer.WriteNull("{{baseName}}");
+
+ {{/isNullable}}
+ {{^isNullable}}
+ if ({{^required}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet && {{/required}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) {{! - }}
+ {
+ string? {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{classname}}.{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}{{nrt!}}.Value{{/isNullable}}{{/required}});
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue);
+ }
+
+ {{/isNullable}}
+ {{/isInnerEnum}}
+ {{^isInnerEnum}}
+ {{#lambda.copyText}}
+ {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}
+ {{/lambda.copyText}}
+ {{#required}}
+ {{#isNullable}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} == null)
+ writer.WriteNull("{{baseName}}");
+ else
+ {
+ var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.Value);
+ {{#allowableValues}}
+ {{#enumVars}}
+ {{#-first}}
+ {{#isString}}
+ if ({{#lambda.paste}}{{/lambda.paste}}RawValue != null){{! we cant use name here because enumVar also has a name property, so use the paste lambda instead }}
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{nameInPascalCase}}{{/lambda.camelcase_sanitize_param}}RawValue);
+ else
+ writer.WriteNull("{{baseName}}");
+ {{/isString}}
+ {{^isString}}
+ writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{nameInPascalCase}}{{/lambda.camelcase_sanitize_param}}RawValue);
+ {{/isString}}
+ {{/-first}}
+ {{/enumVars}}
+ {{/allowableValues}}
+ }
+ {{/isNullable}}
+ {{^isNullable}}
+ var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}});
+ {{#allowableValues}}
+ {{#enumVars}}
+ {{#-first}}
+ {{^isNumeric}}
+ writer.WriteString("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue);
+ {{/isNumeric}}
+ {{#isNumeric}}
+ writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{#lambda.paste}}{{/lambda.paste}}{{/lambda.camelcase_sanitize_param}}RawValue);
+ {{/isNumeric}}
+ {{/-first}}
+ {{/enumVars}}
+ {{/allowableValues}}
+ {{/isNullable}}
+
+ {{/required}}
+ {{^required}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet)
+ {{#isNullable}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option{{nrt!}}.Value != null)
+ {
+ var {{#lambda.paste}}{{/lambda.paste}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.Value{{nrt!}}.Value);
+ writer.{{#lambda.first}}{{#allowableValues}}{{#enumVars}}{{^isNumeric}}WriteString {{/isNumeric}}{{#isNumeric}}WriteNumber {{/isNumeric}}{{/enumVars}}{{/allowableValues}}{{/lambda.first}}("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue);
+ }
+ else
+ writer.WriteNull("{{baseName}}");
+ {{/isNullable}}
+ {{^isNullable}}
+ {
+ var {{#lambda.paste}}{{/lambda.paste}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{nrt!}}.Value);
+ writer.{{#lambda.first}}{{#allowableValues}}{{#enumVars}}{{^isNumeric}}WriteString {{/isNumeric}}{{#isNumeric}}WriteNumber {{/isNumeric}}{{/enumVars}}{{/allowableValues}}{{/lambda.first}}("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue);
+ }
+ {{/isNullable}}
+ {{/required}}
+ {{/isInnerEnum}}
+ {{/isNumeric}}
+ {{/isMap}}
+ {{/isEnum}}
+ {{#isUuid}}
+ {{#lambda.copyText}}
+ writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}});
+ {{/lambda.copyText}}
+ {{#lambda.indent3}}
+ {{>WriteProperty}}{{! prevent indent}}
+ {{/lambda.indent3}}
+ {{/isUuid}}
+ {{^isUuid}}
+ {{^isEnum}}
+ {{^isString}}
+ {{^isBoolean}}
+ {{^isNumeric}}
+ {{^isDate}}
+ {{^isDateTime}}
+ {{#required}}
+ {{#isNullable}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null)
+ {
+ writer.WritePropertyName("{{baseName}}");
+ JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ }
+ else
+ writer.WriteNull("{{baseName}}");
+ {{/isNullable}}
+ {{^isNullable}}
+ writer.WritePropertyName("{{baseName}}");
+ JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ {{/isNullable}}
+ {{/required}}
+ {{^required}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet)
+ {{#isNullable}}
+ if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.Value != null)
+ {
+ writer.WritePropertyName("{{baseName}}");
+ JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ }
+ else
+ writer.WriteNull("{{baseName}}");
+ {{/isNullable}}
+ {{^isNullable}}
+ {
+ writer.WritePropertyName("{{baseName}}");
+ JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
+ }
+ {{/isNullable}}
+ {{/required}}
+ {{/isDateTime}}
+ {{/isDate}}
+ {{/isNumeric}}
+ {{/isBoolean}}
+ {{/isString}}
+ {{/isEnum}}
+ {{/isUuid}}
+ {{/isDiscriminator}}
+ {{/allVars}}
+ {{/lambda.trimLineBreaks}}
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+ }
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache b/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache
new file mode 100644
index 000000000..7e5cf9cb8
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache
@@ -0,0 +1,29 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System.Text.Json;
+
+namespace {{packageName}}.{{apiName}}.{{clientPackage}}
+{
+ ///
+ /// Provides the JsonSerializerOptions.
+ ///
+ {{>visibility}} class JsonSerializerOptionsProvider
+ {
+ ///
+ /// The JsonSerializerOptions.
+ ///
+ public JsonSerializerOptions Options { get; }
+
+ ///
+ /// Instantiates a JsonSerializerOptionsProvider to access the JsonSerializerOptions.
+ ///
+ public JsonSerializerOptionsProvider(JsonSerializerOptions options)
+ {
+ Options = options;
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache b/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache
new file mode 100644
index 000000000..909a68e35
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache
@@ -0,0 +1 @@
+{{#parentModel.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{parent}}{{/lambda.camelcase_sanitize_param}}.{{#lambda.titlecase}}{{baseType}}{{#isArray}}{{{dataFormat}}}{{/isArray}}{{/lambda.titlecase}} {{/parentModel.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#isInherited}}{{^isNew}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{/isNew}}{{#isNew}}{{#isEnum}}{{#isInnerEnum}}{{classname}}.{{{datatypeWithEnum}}}ToJsonValue{{/isInnerEnum}}{{^isInnerEnum}}{{{datatypeWithEnum}}}ValueConverter.ToJsonValue{{/isInnerEnum}}({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{^required}}.Value{{/required}}){{/isEnum}}{{^isEnum}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.ToString(){{/isEnum}}{{/isNew}} {{/isInherited}}{{/isDiscriminator}}{{/allVars}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ModelSignature.mustache b/templates-v7/csharp/libraries/generichost/ModelSignature.mustache
new file mode 100644
index 000000000..09a41b278
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ModelSignature.mustache
@@ -0,0 +1 @@
+{{#model.allVars}}{{^isDiscriminator}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}{{#defaultValue}}{{^isEnum}} = {{^required}}default{{/required}}{{#required}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{.}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/required}}{{/isEnum}}{{#isEnum}} = default{{/isEnum}}{{/defaultValue}}{{^defaultValue}}{{#lambda.first}}{{#isNullable}} = default {{/isNullable}}{{^required}} = default {{/required}}{{/lambda.first}}{{/defaultValue}} {{/isDiscriminator}}{{/model.allVars}}
diff --git a/templates-v7/csharp/libraries/generichost/OAuthToken.mustache b/templates-v7/csharp/libraries/generichost/OAuthToken.mustache
new file mode 100644
index 000000000..23b3cab91
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/OAuthToken.mustache
@@ -0,0 +1,41 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A token constructed with OAuth.
+ ///
+ {{>visibility}} class OAuthToken : TokenBase
+ {
+ private string _raw;
+
+ ///
+ /// Consturcts an OAuthToken object.
+ ///
+ ///
+ ///
+ public OAuthToken(string value, TimeSpan? timeout = null) : base(timeout)
+ {
+ _raw = value;
+ }
+
+ ///
+ /// Places the token in the header.
+ ///
+ ///
+ ///
+ public virtual void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string headerName)
+ {
+ request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _raw);
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache b/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache
new file mode 100644
index 000000000..ff83a5076
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache
@@ -0,0 +1,2 @@
+if (!suppressDefaultLog)
+ Logger.LogError(exception, "An error occurred while deserializing the {code} response.", httpStatusCode);
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/OperationSignature.mustache b/templates-v7/csharp/libraries/generichost/OperationSignature.mustache
new file mode 100644
index 000000000..049717717
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/OperationSignature.mustache
@@ -0,0 +1 @@
+{{#pathParams}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/pathParams}}{{#queryParams}}{{#required}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/required}}{{/queryParams}}{{#bodyParams}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/bodyParams}}{{#queryParams}}{{^required}}Option<{{{dataType}}}{{>NullConditionalParameter}}> {{paramName}}{{#notRequiredOrIsNullable}} = default{{/notRequiredOrIsNullable}}, {{/required}}{{/queryParams}} RequestOptions? requestOptions = default, System.Threading.CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/Option.mustache b/templates-v7/csharp/libraries/generichost/Option.mustache
new file mode 100644
index 000000000..8b33bdb51
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/Option.mustache
@@ -0,0 +1,48 @@
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A wrapper for operation parameters which are not required
+ ///
+ public struct Option
+ {
+ ///
+ /// The value to send to the server
+ ///
+ public TType Value { get; }
+
+ ///
+ /// When true the value will be sent to the server
+ ///
+ internal bool IsSet { get; }
+
+ ///
+ /// A wrapper for operation parameters which are not required
+ ///
+ ///
+ public Option(TType value)
+ {
+ IsSet = true;
+ Value = value;
+ }
+
+ ///
+ /// Implicitly converts this option to the contained type
+ ///
+ ///
+ public static implicit operator TType(Option option) => option.Value;
+
+ ///
+ /// Implicitly converts the provided value to an Option
+ ///
+ ///
+ public static implicit operator Option(TType value) => new Option(value);
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/OptionProperty.mustache b/templates-v7/csharp/libraries/generichost/OptionProperty.mustache
new file mode 100644
index 000000000..d75041887
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/OptionProperty.mustache
@@ -0,0 +1 @@
+new Option<{{#isInnerEnum}}{{^isMap}}{{classname}}.{{/isMap}}{{/isInnerEnum}}{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>(
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/README.client.mustache b/templates-v7/csharp/libraries/generichost/README.client.mustache
new file mode 100644
index 000000000..247cbe399
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/README.client.mustache
@@ -0,0 +1,140 @@
+# Created with Openapi Generator
+
+
+## Creating the library
+Create a config.yaml file similar to what is below, then run the following powershell command to generate the library `java -jar "/openapi-generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar" generate -c config.yaml`
+
+```yaml
+generatorName: csharp
+inputSpec: {{inputSpec}}
+outputDir: out
+
+# https://openapi-generator.tech/docs/generators/csharp
+additionalProperties:
+ packageGuid: '{{packageGuid}}'
+
+# https://openapi-generator.tech/docs/integrations/#github-integration
+# gitHost:
+# gitUserId:
+# gitRepoId:
+
+# https://openapi-generator.tech/docs/globals
+# globalProperties:
+
+# https://openapi-generator.tech/docs/customization/#inline-schema-naming
+# inlineSchemaOptions:
+
+# https://openapi-generator.tech/docs/customization/#name-mapping
+# modelNameMappings:
+# nameMappings:
+
+# https://openapi-generator.tech/docs/customization/#openapi-normalizer
+# openapiNormalizer:
+
+# templateDir: https://openapi-generator.tech/docs/templating/#modifying-templates
+
+# releaseNote:
+```
+
+
+## Using the library in your project
+
+```cs
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using {{packageName}}.Api;
+using {{packageName}}.Client;
+using {{packageName}}.Model;
+using Org.OpenAPITools.Extensions;
+
+namespace YourProject
+{
+ public class Program
+ {
+ public static async Task Main(string[] args)
+ {
+ var host = CreateHostBuilder(args).Build();
+ {{#apiInfo}}
+ {{#apis}}
+ {{#-first}}
+ var api = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+ {{#operations}}
+ {{#-first}}
+ {{#operation}}
+ {{#-first}}
+ {{interfacePrefix}}{{operationId}}ApiResponse apiResponse = await api.{{operationId}}Async("todo");
+ {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}} model = apiResponse.Ok();
+ {{/-first}}
+ {{/operation}}
+ {{/-first}}
+ {{/operations}}
+ {{/-first}}
+ {{/apis}}
+ {{/apiInfo}}
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)
+ .Configure{{apiName}}((context, services, options) =>
+ {
+ {{#authMethods}}
+ {{#-first}}
+ // The type of token here depends on the api security specifications
+ // Available token types are ApiKeyToken, BearerToken, HttpSigningToken, and OAuthToken.
+ BearerToken token = new("");
+ options.AddTokens(token);
+
+ // optionally choose the method the tokens will be provided with, default is RateLimitProvider
+ options.UseProvider, BearerToken>();
+
+ {{/-first}}
+ {{/authMethods}}
+ options.ConfigureJsonOptions((jsonOptions) =>
+ {
+ // your custom converters if any
+ });
+
+ options.Add{{apiName}}HttpClients(client =>
+ {
+ // client configuration
+ }, builder =>
+ {
+ builder
+ .AddRetryPolicy(2)
+ .AddTimeoutPolicy(TimeSpan.FromSeconds(5))
+ .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30));
+ // add whatever middleware you prefer
+ }
+ );
+ });
+ }
+}
+```
+
+## Questions
+
+- What about HttpRequest failures and retries?
+ Configure Polly in the IHttpClientBuilder
+- How are tokens used?
+ Tokens are provided by a TokenProvider class. The default is RateLimitProvider which will perform client side rate limiting.
+ Other providers can be used with the UseProvider method.
+- Does an HttpRequest throw an error when the server response is not Ok?
+ It depends how you made the request. If the return type is ApiResponse no error will be thrown, though the Content property will be null.
+ StatusCode and ReasonPhrase will contain information about the error.
+ If the return type is T, then it will throw. If the return type is TOrDefault, it will return null.
+- How do I validate requests and process responses?
+ Use the provided On and After partial methods in the api classes.
+
+## Api Information
+- appName: {{appName}}
+- appVersion: {{appVersion}}
+- appDescription: {{appDescription}}
+
+## Build
+This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
+
+- SDK version: {{packageVersion}}
+{{^hideGenerationTimestamp}}
+- Build date: {{generatedDate}}
+{{/hideGenerationTimestamp}}
+- Generator version: {{generatorVersion}}
+- Build package: {{generatorClass}}
diff --git a/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache b/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache
new file mode 100644
index 000000000..b1798c98e
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache
@@ -0,0 +1,9 @@
+{{#useSourceGeneration}}
+
+ ///
+ /// The {{classname}}SerializationContext
+ ///
+ [JsonSourceGenerationOptions(WriteIndented = true, GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
+ [JsonSerializable(typeof({{classname}}))]
+ {{>visibility}} partial class {{classname}}SerializationContext : JsonSerializerContext { }
+{{/useSourceGeneration}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/TokenBase.mustache b/templates-v7/csharp/libraries/generichost/TokenBase.mustache
new file mode 100644
index 000000000..05f06b4a7
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/TokenBase.mustache
@@ -0,0 +1,73 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// The base for all tokens.
+ ///
+ {{>visibility}} abstract class TokenBase
+ {
+ private DateTime _nextAvailable = DateTime.UtcNow;
+ private object _nextAvailableLock = new object();
+ private readonly System.Timers.Timer _timer = new System.Timers.Timer();
+
+
+ internal TimeSpan? Timeout { get; set; }
+ internal delegate void TokenBecameAvailableEventHandler(object sender);
+ internal event TokenBecameAvailableEventHandler{{nrt?}} TokenBecameAvailable;
+
+
+ ///
+ /// Initialize a TokenBase object.
+ ///
+ ///
+ internal TokenBase(TimeSpan? timeout = null)
+ {
+ Timeout = timeout;
+
+ if (Timeout != null)
+ StartTimer(Timeout.Value);
+ }
+
+
+ ///
+ /// Starts the token's timer
+ ///
+ ///
+ internal void StartTimer(TimeSpan timeout)
+ {
+ Timeout = timeout;
+ _timer.Interval = Timeout.Value.TotalMilliseconds;
+ _timer.Elapsed += OnTimer;
+ _timer.AutoReset = true;
+ _timer.Start();
+ }
+
+ ///
+ /// Returns true while the token is rate limited.
+ ///
+ public bool IsRateLimited => _nextAvailable > DateTime.UtcNow;
+
+ ///
+ /// Triggered when the server returns status code TooManyRequests
+ /// Once triggered the local timeout will be extended an arbitrary length of time.
+ ///
+ public void BeginRateLimit()
+ {
+ lock(_nextAvailableLock)
+ _nextAvailable = DateTime.UtcNow.AddSeconds(5);
+ }
+
+ private void OnTimer(object{{nrt?}} sender, System.Timers.ElapsedEventArgs e)
+ {
+ if (TokenBecameAvailable != null && !IsRateLimited)
+ TokenBecameAvailable.Invoke(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/TokenContainer.mustache b/templates-v7/csharp/libraries/generichost/TokenContainer.mustache
new file mode 100644
index 000000000..24c84a551
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/TokenContainer.mustache
@@ -0,0 +1,39 @@
+//
+{{partial_header}}
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System.Linq;
+using System.Collections.Generic;
+
+namespace {{packageName}}.{{clientPackage}}
+{
+ ///
+ /// A container for a collection of tokens.
+ ///
+ ///
+ {{>visibility}} sealed class TokenContainer where TTokenBase : TokenBase
+ {
+ ///
+ /// The collection of tokens
+ ///
+ public List Tokens { get; } = new List();
+
+ ///
+ /// Instantiates a TokenContainer
+ ///
+ public TokenContainer()
+ {
+ }
+
+ ///
+ /// Instantiates a TokenContainer
+ ///
+ ///
+ public TokenContainer(global::System.Collections.Generic.IEnumerable tokens)
+ {
+ Tokens = tokens.ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/TokenProvider.mustache b/templates-v7/csharp/libraries/generichost/TokenProvider.mustache
new file mode 100644
index 000000000..9fc5f3ea0
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/TokenProvider.mustache
@@ -0,0 +1,39 @@
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using {{packageName}}.{{clientPackage}};
+
+namespace {{packageName}}
+{
+ ///
+ /// A class which will provide tokens.
+ ///
+ {{>visibility}} abstract class TokenProvider where TTokenBase : TokenBase
+ {
+ ///
+ /// The array of tokens.
+ ///
+ protected TTokenBase[] _tokens;
+
+ internal abstract System.Threading.Tasks.ValueTask GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}});
+
+ ///
+ /// Instantiates a TokenProvider.
+ ///
+ ///
+ public TokenProvider(IEnumerable tokens)
+ {
+ _tokens = tokens.ToArray();
+
+ if (_tokens.Length == 0)
+ throw new ArgumentException("You did not provide any tokens.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache b/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache
new file mode 100644
index 000000000..8918b8cf4
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache
@@ -0,0 +1,7 @@
+// {{{name}}} ({{{dataType}}}) pattern
+Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}});
+{{#lambda.copy}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null && {{/lambda.copy}}
+if ({{#lambda.first}}{{^required}}{{#lambda.paste}}{{/lambda.paste}} {{/required}}{{#isNullable}}{{#lambda.paste}}{{/lambda.paste}}{{/isNullable}}{{/lambda.first}}!regex{{{name}}}.Match(this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}}{{#isUuid}}.ToString(){{#lambda.first}}{{^required}}{{nrt!}} {{/required}}{{#isNullable}}! {{/isNullable}}{{/lambda.first}}{{/isUuid}}).Success)
+{
+ yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" });
+}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache b/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache
new file mode 100644
index 000000000..3a35bdd49
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache
@@ -0,0 +1,88 @@
+{{#lambda.trimLineBreaks}}
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using Microsoft.Extensions.Logging;
+using System.Text.Json;
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{apiName}}.Client;
+using {{packageName}}.{{modelPackage}};
+
+namespace {{packageName}}.{{apiName}}.Handlers
+{
+ ///
+ /// Interface for deserializing {{apiName}} webhooks or verify its HMAC signature.
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{apiName}}Handler
+ {
+ ///
+ /// Returns the to deserialize the json payload from the webhook. This is initialized in the .
+ ///
+ {{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider JsonSerializerOptionsProvider { get; }
+ {{#models}}
+ {{#model.vendorExtensions.x-webhook-root}}
+
+ ///
+ /// Uses to attempt to deserialize .
+ ///
+ /// The full webhook payload.
+ ///
+ {{model.name}}? Deserialize{{model.name}}(string json);
+ {{/model.vendorExtensions.x-webhook-root}}
+ {{/models}}
+
+ ///
+ /// Verifies the HMAC signature of the webhook json payload.
+ ///
+ /// The full webhook payload.
+ /// The signature from the webhook.
+ /// True if the HMAC signature is valid
+ bool IsValidHmacSignature(string json, string hmacSignature);
+ }
+
+ ///
+ /// Handler utility function used to deserialize {{apiName}} or verify the HMAC signature of the webhook.
+ ///
+ {{>visibility}} partial class {{apiName}}Handler : {{interfacePrefix}}{{apiName}}Handler
+ {
+ ///
+ /// The `ADYEN_HMAC_KEY` configured in .
+ ///
+ private readonly string? _adyenHmacKey;
+
+ ///
+ public {{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider JsonSerializerOptionsProvider { get; }
+
+ ///
+ /// Initializes the handler utility for deserializing {{apiName}} or verify its HMAC signature.
+ ///
+ /// .
+ /// which contains the HMACKey configured in .
+ public {{apiName}}Handler({{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider jsonSerializerOptionsProvider, ITokenProvider hmacKeyProvider = null)
+ {
+ JsonSerializerOptionsProvider = jsonSerializerOptionsProvider;
+ }
+
+ {{#models}}
+ {{#model.vendorExtensions.x-webhook-root}}
+ ///
+ public {{model.name}}? Deserialize{{model.name}}(string json)
+ {
+ return JsonSerializer.Deserialize<{{model.name}}>(json, JsonSerializerOptionsProvider.Options);
+ }
+
+ {{/model.vendorExtensions.x-webhook-root}}
+ {{/models}}
+
+ ///
+ public bool IsValidHmacSignature(string json, string hmacSignature)
+ {
+ return {{packageName}}.{{corePackageName}}.Utilities.HmacValidatorUtility.IsHmacSignatureValid(hmacSignature, _adyenHmacKey, json);
+ }
+ }
+}
+{{/lambda.trimLineBreaks}}
diff --git a/templates-v7/csharp/libraries/generichost/WriteProperty.mustache b/templates-v7/csharp/libraries/generichost/WriteProperty.mustache
new file mode 100644
index 000000000..8fe669906
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/WriteProperty.mustache
@@ -0,0 +1,10 @@
+{{#required}}
+{{>WritePropertyHelper}}
+
+{{/required}}
+{{^required}}
+if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet)
+ {{#lambda.indent1}}
+ {{>WritePropertyHelper}}{{! prevent indent}}
+ {{/lambda.indent1}}
+{{/required}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache b/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache
new file mode 100644
index 000000000..04270093a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache
@@ -0,0 +1,10 @@
+{{#isNullable}}
+if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{/required}} != null)
+ {{#lambda.paste}}{{/lambda.paste}}
+else
+ writer.WriteNull("{{baseName}}");
+{{/isNullable}}
+{{^isNullable}}
+{{#lambda.paste}}
+{{/lambda.paste}}
+{{/isNullable}}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/api.mustache b/templates-v7/csharp/libraries/generichost/api.mustache
new file mode 100644
index 000000000..67264bf5a
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/api.mustache
@@ -0,0 +1,653 @@
+{{#lambda.trimLineBreaks}}
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Collections.Generic;
+{{#net80OrLater}}
+{{#lambda.uniqueLines}}
+{{#operations}}
+{{#operation}}
+{{#vendorExtensions.x-set-cookie}}
+using System.Linq;
+{{/vendorExtensions.x-set-cookie}}
+{{/operation}}
+{{/operations}}
+{{/lambda.uniqueLines}}
+{{/net80OrLater}}
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using {{packageName}}.{{corePackageName}};
+using {{packageName}}.{{corePackageName}}.Auth;
+using {{packageName}}.{{corePackageName}}.Client;
+using {{packageName}}.{{corePackageName}}.Client.Extensions;
+using {{packageName}}.{{apiName}}.{{clientPackage}};
+{{#hasImport}}
+using {{packageName}}.{{modelPackage}};
+{{/hasImport}}
+{{^netStandard}}
+using System.Diagnostics.CodeAnalysis;
+{{/netStandard}}
+
+namespace {{packageName}}.{{apiPackage}}
+{
+ {{#operations}}
+ ///
+ /// Represents a collection of functions to interact with the API endpoints.
+ /// This class is registered as transient.
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{classname}} : {{interfacePrefix}}{{packageName}}ApiService
+ {
+ ///
+ /// The class containing the events.
+ ///
+ {{classname}}Events Events { get; }
+
+ {{#operation}}
+ ///
+ /// {{summary}}
+ ///
+ ///
+ /// {{notes}}
+ ///
+ /// Thrown when fails to make API call.
+ {{#allParams}}
+ /// {{{description}}}{{#isHeaderParam}} - Pass this header parameter using .{{/isHeaderParam}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
+ {{/allParams}}
+ /// .
+ /// .
+ /// of .
+ {{#isDeprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/isDeprecated}}
+ Task<{{interfacePrefix}}{{operationId}}ApiResponse> {{operationId}}Async({{>OperationSignature}});
+
+ {{/operation}}
+ }
+ {{#operation}}
+ {{^vendorExtensions.x-duplicates}}
+ {{#responses}}
+ {{#-first}}
+
+ ///
+ /// The , wraps .
+ ///
+ {{>visibility}} interface {{interfacePrefix}}{{operationId}}ApiResponse : {{#lambda.joinWithComma}}{{packageName}}.{{corePackageName}}.{{clientPackage}}.{{interfacePrefix}}ApiResponse {{#responses}}{{#dataType}}{{interfacePrefix}}{{vendorExtensions.x-http-status}}<{{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}> {{/dataType}}{{/responses}}{{/lambda.joinWithComma}}
+ {
+ {{#responses}}
+ {{#vendorExtensions.x-http-status-is-default}}
+ ///
+ /// Returns true if the response is the default response type.
+ ///
+ ///
+ bool Is{{vendorExtensions.x-http-status}} { get; }
+ {{/vendorExtensions.x-http-status-is-default}}
+ {{^vendorExtensions.x-http-status-is-default}}
+ ///
+ /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}}.
+ ///
+ ///
+ bool Is{{vendorExtensions.x-http-status}} { get; }
+ {{/vendorExtensions.x-http-status-is-default}}
+ {{^-last}}
+
+ {{/-last}}
+ {{/responses}}
+ }
+ {{/-first}}
+ {{/responses}}
+ {{/vendorExtensions.x-duplicates}}
+ {{/operation}}
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints.
+ ///
+ {{>visibility}} class {{classname}}Events
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#operation}}
+ ///
+ /// The event raised after the server response.
+ ///
+ public event EventHandler{{nrt?}} On{{operationId}};
+
+ ///
+ /// The event raised after an error querying the server.
+ ///
+ public event EventHandler{{nrt?}} OnError{{operationId}};
+
+ internal void ExecuteOn{{operationId}}({{#vendorExtensions.x-duplicates}}{{.}}{{/vendorExtensions.x-duplicates}}{{^vendorExtensions.x-duplicates}}{{classname}}{{/vendorExtensions.x-duplicates}}.{{operationId}}ApiResponse apiResponse)
+ {
+ On{{operationId}}?.Invoke(this, new ApiResponseEventArgs(apiResponse));
+ }
+
+ internal void ExecuteOnError{{operationId}}(Exception exception)
+ {
+ OnError{{operationId}}?.Invoke(this, new ExceptionEventArgs(exception));
+ }
+
+ {{/operation}}
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ ///
+ /// Represents a collection of functions to interact with the API endpoints.
+ ///
+ {{>visibility}} sealed partial class {{classname}} : {{interfacePrefix}}{{classname}}
+ {
+ private JsonSerializerOptions _jsonSerializerOptions;
+
+ ///
+ /// The logger factory.
+ ///
+ public ILoggerFactory LoggerFactory { get; }
+
+ ///
+ /// The logger.
+ ///
+ public ILogger<{{classname}}> Logger { get; }
+
+ ///
+ /// The HttpClient.
+ ///
+ public System.Net.Http.HttpClient HttpClient { get; }
+
+ ///
+ /// The class containing the events.
+ ///
+ public {{classname}}Events Events { get; }{{#hasApiKeyMethods}}
+
+ ///
+ /// A token provider of type .
+ ///
+ public ITokenProvider ApiKeyProvider { get; }{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}
+
+ ///
+ /// A token provider of type .
+ ///
+ public ITokenProvider BearerTokenProvider { get; }{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}}
+
+ ///
+ /// A token provider of type .
+ ///
+ public ITokenProvider HttpSignatureTokenProvider { get; }{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}
+
+ ///
+ /// A token provider of type .
+ ///
+ public ITokenProvider OauthTokenProvider { get; }{{/hasOAuthMethods}}
+
+ {{#net80OrLater}}
+ {{#lambda.unique}}
+ {{#operation}}
+ {{#vendorExtensions.x-set-cookie}}
+ ///
+ /// The token cookie container.
+ ///
+ public {{packageName}}.{{corePackageName}}.Auth.CookieContainer CookieContainer { get; }
+
+ {{/vendorExtensions.x-set-cookie}}
+ {{/operation}}
+ {{/lambda.unique}}
+ {{/net80OrLater}}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public {{classname}}(ILogger<{{classname}}> logger, ILoggerFactory loggerFactory, System.Net.Http.HttpClient httpClient, JsonSerializerOptionsProvider jsonSerializerOptionsProvider, {{classname}}Events {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}}{{#hasApiKeyMethods}},
+ ITokenProvider apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}},
+ ITokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}},
+ ITokenProvider httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}},
+ ITokenProvider oauthTokenProvider{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}},
+ {{packageName}}.{{clientPackage}}.CookieContainer cookieContainer{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}})
+ {
+ _jsonSerializerOptions = jsonSerializerOptionsProvider.Options;
+ LoggerFactory = loggerFactory;
+ Logger = logger == null ? LoggerFactory.CreateLogger<{{classname}}>() : logger;
+ HttpClient = httpClient;
+ Events = {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}};{{#hasApiKeyMethods}}
+ ApiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}
+ BearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}}
+ HttpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}
+ OauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}}
+ CookieContainer = cookieContainer;{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}}
+ }
+
+ {{#operation}}
+ ///
+ /// {{summary}} {{notes}}
+ ///
+ /// Thrown when fails to make API call.
+ {{#allParams}}
+ /// {{{description}}}{{#isBodyParam}}{{/isBodyParam}}{{^required}} ({{#defaultValue}}default to {{.}}{{/defaultValue}}){{/required}}{{#isHeaderParam}} Pass this header parameter in .{{/isHeaderParam}}
+ {{/allParams}}
+ /// .
+ /// .
+ /// of - If 200 OK response, wraps the when `TryDeserializeOk(...)` is called.
+ public async Task<{{interfacePrefix}}{{operationId}}ApiResponse> {{operationId}}Async({{>OperationSignature}})
+ {
+ {{#lambda.trimLineBreaks}}
+ UriBuilder uriBuilder = new UriBuilder();
+
+ try
+ {
+ using (HttpRequestMessage httpRequestMessage = new HttpRequestMessage())
+ {
+ {{^servers}}
+ uriBuilder.Host = HttpClient.BaseAddress{{nrt!}}.Host;
+ uriBuilder.Port = HttpClient.BaseAddress.Port;
+ uriBuilder.Scheme = HttpClient.BaseAddress.Scheme;
+ uriBuilder.Path = HttpClient.BaseAddress.AbsolutePath == "/"
+ ? "{{{path}}}"
+ : string.Concat(HttpClient.BaseAddress.AbsolutePath, "{{{path}}}");
+ {{/servers}}
+ {{#servers}}
+ {{#-first}}
+ Uri url = httpRequestMessage.RequestUri = new Uri("{{url}}");
+ uriBuilder.Host = url.Authority;
+ uriBuilder.Scheme = url.Scheme;
+ uriBuilder.Path = url.AbsolutePath;
+ {{/-first}}
+ {{/servers}}
+ {{#constantParams}}
+ {{#isPathParam}}
+ // Set client side default value of Path Param "{{baseName}}".
+ uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString(ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}}))); // Constant path parameter
+ {{/isPathParam}}
+ {{/constantParams}}
+ {{#pathParams}}
+ uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));
+ {{#-last}}
+
+ {{/-last}}
+ {{/pathParams}}
+ {{#queryParams}}
+ {{#-first}}
+
+ System.Collections.Specialized.NameValueCollection parseQueryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
+ {{/-first}}
+ {{/queryParams}}
+ {{^queryParams}}
+ {{#authMethods}}
+ {{#isApiKey}}
+ {{#isKeyInQuery}}
+
+ System.Collections.Specialized.NameValueCollection parseQueryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
+ {{/isKeyInQuery}}
+ {{/isApiKey}}
+ {{/authMethods}}
+ {{/queryParams}}
+ {{#queryParams}}
+ {{#required}}
+ {{#-first}}
+
+ {{/-first}}
+ parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}});
+ {{/required}}
+ {{/queryParams}}
+
+ {{#constantParams}}
+ {{#isQueryParam}}
+ // Set client side default value of Query Param "{{baseName}}".
+ parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}}); // Constant query parameter
+ {{/isQueryParam}}
+ {{/constantParams}}
+ {{#queryParams}}
+ {{^required}}
+ if ({{paramName}}.IsSet)
+ parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}}.Value);
+
+ {{/required}}
+ {{#-last}}
+ uriBuilder.Query = parseQueryString.ToString();
+
+ {{/-last}}
+ {{/queryParams}}
+ // Adds headers to the HttpRequestMessage header, these can be set in the RequestOptions (Idempotency-Key etc.)
+ requestOptions?.AddHeadersToHttpRequestMessage(httpRequestMessage);
+ {{#formParams}}
+ {{#-first}}
+ MultipartContent multipartContent = new MultipartContent();
+
+ httpRequestMessage.Content = multipartContent;
+
+ List> formParameters = new List>();
+
+ multipartContent.Add(new FormUrlEncodedContent(formParameters));{{/-first}}{{^isFile}}{{#required}}
+
+ formParameters.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));
+
+ {{/required}}
+ {{^required}}
+ if ({{paramName}}.IsSet)
+ formParameters.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}}.Value)));
+
+ {{/required}}
+ {{/isFile}}
+ {{#isFile}}
+ {{#required}}
+ multipartContent.Add(new StreamContent({{paramName}}));
+
+ {{/required}}
+ {{^required}}
+ if ({{paramName}}.IsSet)
+ multipartContent.Add(new StreamContent({{paramName}}.Value));
+
+ {{/required}}
+ {{/isFile}}
+ {{/formParams}}
+ {{#bodyParam}}
+ httpRequestMessage.Content = ({{paramName}} as object) is System.IO.Stream stream
+ ? httpRequestMessage.Content = new StreamContent(stream)
+ : httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize({{paramName}}, _jsonSerializerOptions));
+ {{/bodyParam}}
+ {{#authMethods}}
+
+ {{#isApiKey}}
+ {{! Only use API Keys that are appended to the HTTP-headers }}
+ {{^isKeyInCookie}}
+ {{#isKeyInHeader}}
+ // Add authorization token to the HttpRequestMessage header
+ ApiKeyProvider.Get().AddTokenToHttpRequestMessageHeader(httpRequestMessage);
+
+ {{/isKeyInHeader}}
+ {{/isKeyInCookie}}
+ {{/isApiKey}}
+ {{/authMethods}}
+ httpRequestMessage.RequestUri = uriBuilder.Uri;
+ {{#authMethods}}
+ {{#isBasicBearer}}
+ // Not supported, you'll have to add the BearerToken class and fetch is similar to the ApiKeyProvider.
+ BearerToken bearerToken{{-index}} = (BearerToken) await BearerTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false);
+ bearerToken{{-index}}.UseInHeader(httpRequestMessage, "{{keyParamName}}");
+ {{/isBasicBearer}}
+ {{#isOAuth}}
+ // Not supported (yet), you'll have to add the BearerToken class and fetch is similar to the ApiKeyProvider.
+ OAuthToken oauthToken{{-index}} = (OAuthToken) await OauthTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false);
+ oauthToken{{-index}}.UseInHeader(httpRequestMessage, "{{keyParamName}}");
+ {{/isOAuth}}
+ {{#isHttpSignature}}
+
+ HttpSignatureToken httpSignatureToken{{-index}} = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false);
+
+ if (httpRequestMessage.Content != null) {
+ string requestBody = await httpRequestMessage.Content.ReadAsStringAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false);
+
+ httpSignatureToken{{-index}}.UseInHeader(httpRequestMessage, requestBody, cancellationToken);
+ }
+ {{/isHttpSignature}}
+ {{/authMethods}}
+ {{#consumes}}
+ {{#-first}}
+
+ {{=<% %>=}}
+ string[] contentTypes = new string[] {<%/-first%>
+ <%={{ }}=%>
+ "{{{mediaType}}}"{{^-last}},{{/-last}}{{#-last}}
+ };
+ {{/-last}}
+ {{/consumes}}
+ {{#consumes}}
+ {{#-first}}
+
+ httpRequestMessage.AddUserAgentToHeaders();
+ httpRequestMessage.AddLibraryNameToHeader();
+ httpRequestMessage.AddLibraryVersionToHeader();
+
+ string{{nrt?}} contentType = ClientUtils.SelectHeaderContentType(contentTypes);
+
+ if (contentType != null && httpRequestMessage.Content != null)
+ httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
+
+ {{/-first}}
+ {{/consumes}}
+ {{#produces}}
+ {{#-first}}
+
+ {{=<% %>=}}
+ string[] accepts = new string[] {<%/-first%>
+ <%={{ }}=%>
+ "{{{mediaType}}}"{{^-last}},{{/-last}}{{#-last}}
+ };
+ {{/-last}}
+ {{/produces}}
+ {{#produces}}
+ {{#-first}}
+
+ string{{nrt?}} accept = ClientUtils.SelectHeaderAccept(accepts);
+
+ if (accept != null)
+ httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
+ {{/-first}}
+ {{/produces}}
+ {{#net60OrLater}}
+
+ httpRequestMessage.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}};
+ {{/net60OrLater}}
+ {{^net60OrLater}}
+ httpRequestMessage.Method = new HttpMethod("{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}}");
+ {{/net60OrLater}}
+
+ DateTime requestedAt = DateTime.UtcNow;
+
+ using (HttpResponseMessage httpResponseMessage = await HttpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false))
+ {
+ ILogger<{{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse> apiResponseLogger = LoggerFactory.CreateLogger<{{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse>();
+ {{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse apiResponse;
+
+ switch ((int)httpResponseMessage.StatusCode) {
+ {{#responses}}
+ {{#isBinary}}
+ case ({{code}}):
+ {{/isBinary}}
+ {{/responses}}
+ {{#responses}}
+ {{#isBinary}}
+ {{#-first}}
+ {
+ byte[] responseBytesArray = await httpResponseMessage.Content.ReadAsByteArrayAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false);
+ System.IO.Stream responseContentStream = new System.IO.MemoryStream(responseBytesArray);
+ apiResponse = new{{^net60OrLater}} {{operationId}}ApiResponse{{/net60OrLater}}(apiResponseLogger, httpRequestMessage, httpResponseMessage, responseContentStream, "{{{path}}}", requestedAt, _jsonSerializerOptions);
+
+ break;
+ }
+ {{/-first}}
+ {{/isBinary}}
+ {{/responses}}
+ default: {
+ string responseContent = await httpResponseMessage.Content.ReadAsStringAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false);
+ apiResponse = new{{^net60OrLater}} {{operationId}}ApiResponse{{/net60OrLater}}(apiResponseLogger, httpRequestMessage, httpResponseMessage, responseContent, "{{{path}}}", requestedAt, _jsonSerializerOptions);
+
+ break;
+ }
+ }
+
+ Events.ExecuteOn{{operationId}}(apiResponse);
+ {{#net80OrLater}}
+ {{#responses}}
+ {{#vendorExtensions.x-set-cookie}}
+ if (httpResponseMessage.StatusCode == (HttpStatusCode) {{code}} && httpResponseMessage.Headers.TryGetValues("Set-Cookie", out var cookieHeaders))
+ {
+ foreach(string cookieHeader in cookieHeaders)
+ {
+ IList setCookieHeaderValues = Microsoft.Net.Http.Headers.SetCookieHeaderValue.ParseList(cookieHeaders.ToArray());
+
+ foreach(Microsoft.Net.Http.Headers.SetCookieHeaderValue setCookieHeaderValue in setCookieHeaderValues)
+ {
+ Cookie cookie = new Cookie(setCookieHeaderValue.Name.ToString(), setCookieHeaderValue.Value.ToString())
+ {
+ HttpOnly = setCookieHeaderValue.HttpOnly
+ };
+
+ if (setCookieHeaderValue.Expires.HasValue)
+ cookie.Expires = setCookieHeaderValue.Expires.Value.UtcDateTime;
+
+ if (setCookieHeaderValue.Path.HasValue)
+ cookie.Path = setCookieHeaderValue.Path.Value;
+
+ if (setCookieHeaderValue.Domain.HasValue)
+ cookie.Domain = setCookieHeaderValue.Domain.Value;
+
+ CookieContainer.Value.Add(new Uri($"{uriBuilder.Scheme}://{uriBuilder.Host}"), cookie);
+ }
+ }
+ }
+
+ {{/vendorExtensions.x-set-cookie}}
+ {{/responses}}
+ {{/net80OrLater}}
+ return apiResponse;
+ }
+ }
+ }
+ catch(Exception exception)
+ {
+ Events.ExecuteOnError{{operationId}}(exception);
+ throw;
+ }
+ {{/lambda.trimLineBreaks}}
+ }
+ {{^vendorExtensions.x-duplicates}}
+ {{#responses}}
+ {{#-first}}
+
+ ///
+ /// The .
+ ///
+ {{>visibility}} partial class {{operationId}}ApiResponse : {{packageName}}.{{corePackageName}}.{{clientPackage}}.ApiResponse, {{interfacePrefix}}{{operationId}}ApiResponse
+ {
+ ///
+ /// The logger for .
+ ///
+ public ILogger<{{operationId}}ApiResponse> Logger { get; }
+
+ ///
+ /// The .
+ ///
+ /// .
+ /// .
+ /// .
+ /// The raw data.
+ /// The path used when making the request.
+ /// The DateTime.UtcNow when the request was retrieved.
+ ///
+ public {{operationId}}ApiResponse(ILogger<{{operationId}}ApiResponse> logger, System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, string rawContent, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) : base(httpRequestMessage, httpResponseMessage, rawContent, path, requestedAt, jsonSerializerOptions)
+ {
+ Logger = logger;
+ OnCreated(httpRequestMessage, httpResponseMessage);
+ }
+
+ ///
+ /// The .
+ ///
+ /// .
+ /// .
+ /// .
+ /// The raw binary stream (only set for binary responses).
+ /// The path used when making the request.
+ /// The DateTime.UtcNow when the request was retrieved.
+ ///
+ public {{operationId}}ApiResponse(ILogger<{{operationId}}ApiResponse> logger, System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, System.IO.Stream contentStream, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) : base(httpRequestMessage, httpResponseMessage, contentStream, path, requestedAt, jsonSerializerOptions)
+ {
+ Logger = logger;
+ OnCreated(httpRequestMessage, httpResponseMessage);
+ }
+
+ partial void OnCreated(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage);
+ {{#responses}}
+
+ {{#vendorExtensions.x-http-status-is-default}}
+ ///
+ /// Returns true if the response is the default response type.
+ ///
+ ///
+ public bool Is{{vendorExtensions.x-http-status}} => {{#vendorExtensions.x-only-default}}true{{/vendorExtensions.x-only-default}}{{^vendorExtensions.x-only-default}}{{#lambda.joinConditions}}{{#responses}}{{^vendorExtensions.x-http-status-is-default}}!Is{{vendorExtensions.x-http-status}} {{/vendorExtensions.x-http-status-is-default}}{{/responses}}{{/lambda.joinConditions}}{{/vendorExtensions.x-only-default}};
+ {{/vendorExtensions.x-http-status-is-default}}
+ {{^vendorExtensions.x-http-status-is-default}}
+ ///
+ /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}}.
+ ///
+ ///
+ {{#vendorExtensions.x-http-status-range}}
+ public bool Is{{vendorExtensions.x-http-status}}
+ {
+ get
+ {
+ int statusCode = (int)StatusCode;
+ return {{vendorExtensions.x-http-status-range}}00 >= statusCode && {{vendorExtensions.x-http-status-range}}99 <= statusCode;
+ }
+ }
+ {{/vendorExtensions.x-http-status-range}}
+ {{^vendorExtensions.x-http-status-range}}
+ public bool Is{{vendorExtensions.x-http-status}} => {{code}} == (int)StatusCode;
+ {{/vendorExtensions.x-http-status-range}}
+ {{/vendorExtensions.x-http-status-is-default}}
+ {{#dataType}}
+
+ ///
+ /// Deserializes the response if the response is {{code}} {{vendorExtensions.x-http-status}}.
+ ///
+ ///
+ public {{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{vendorExtensions.x-http-status}}()
+ {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.indent4}}
+ {{>AsModel}}{{! prevent indent}}
+ {{/lambda.indent4}}
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ ///
+ /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}} and the deserialized response is not null.
+ ///
+ ///
+ ///
+ public bool TryDeserialize{{vendorExtensions.x-http-status}}Response({{#net60OrLater}}[NotNullWhen(true)]{{/net60OrLater}}out {{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} result)
+ {
+ result = null;
+
+ try
+ {
+ result = {{vendorExtensions.x-http-status}}();
+ }
+ catch (Exception exception)
+ {
+ OnDeserializationError(exception, (HttpStatusCode){{#vendorExtensions.x-http-status-range}}{{.}}{{/vendorExtensions.x-http-status-range}}{{^vendorExtensions.x-http-status-range}}{{code}}{{/vendorExtensions.x-http-status-range}});
+ }
+
+ return result != null;
+ }
+ {{/dataType}}
+ {{#-last}}
+
+ private void OnDeserializationError(Exception exception, HttpStatusCode httpStatusCode)
+ {
+ bool suppressDefaultLog = false;
+ OnDeserializationError(ref suppressDefaultLog, exception, httpStatusCode);
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.indent4}}
+ {{>OnDeserializationError}}{{! prevent indent}}
+ {{/lambda.indent4}}
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ partial void OnDeserializationError(ref bool suppressDefaultLog, Exception exception, HttpStatusCode httpStatusCode);
+ {{/-last}}
+ {{/responses}}
+ }
+
+ {{/-first}}
+ {{/responses}}
+ {{/vendorExtensions.x-duplicates}}
+ {{/operation}}
+ }
+ {{/operations}}
+}
+{{/lambda.trimLineBreaks}}
diff --git a/templates-v7/csharp/libraries/generichost/api_doc.mustache b/templates-v7/csharp/libraries/generichost/api_doc.mustache
new file mode 100644
index 000000000..feb36ebc0
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/api_doc.mustache
@@ -0,0 +1,91 @@
+## {{packageName}}.{{apiPackage}}.{{classname}}{{#description}}
+{{.}}{{/description}}
+
+#### API Base-Path: **{{{basePath}}}**
+
+
+#### Authorization: {{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}{{#isApiKey}}{{^isKeyInCookie}}{{#isKeyInHeader}}{{{name}}}{{/isKeyInHeader}}{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}}
+
+
+#### Initialization
+
+
+```csharp
+using {{packageName}}.{{corePackageName}}.Options;
+using {{packageName}}.{{apiName}}.Extensions;
+using {{packageName}}.{{apiName}}.Services;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+IHost host = Host.CreateDefaultBuilder()
+ .Configure{{apiName}}((context, services, config) =>
+ {
+ config.ConfigureAdyenOptions(options =>
+ {
+ // Set your ADYEN_API_KEY or use get it from your environment using context.Configuration["ADYEN_API_KEY"].
+ options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"];
+
+ // Set your environment, e.g. `Test` or `Live`.
+ options.Environment = AdyenEnvironment.Test;
+
+ // For the Live environment, additionally include your LiveEndpointUrlPrefix.
+ options.LiveEndpointUrlPrefix = "your-live-endpoint-url-prefix";
+ });
+ })
+ .Build();
+
+ // You can inject this service in your constructor.
+ {{interfacePrefix}}{{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}} = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
+```
+
+| Method | HTTP request | Description |
+|--------|--------------|-------------|
+{{#operations}}
+{{#operation}}
+| [**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{summary}} |
+{{/operation}}
+{{/operations}}
+
+{{#operations}}
+{{#operation}}
+
+### **{{httpMethod}}** **{{{path}}}**
+
+{{{summary}}}
+
+#### Parameters
+{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
+| Name | Type | Description |
+|------|------|-------------|
+{{/-last}}
+{{/allParams}}
+{{#allParams}}
+| **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}[{{dataType}}]{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}**{{dataType}}**{{/isFile}}{{/isPrimitiveType}} | {{description}} |
+{{/allParams}}
+
+#### Example usage
+
+```csharp
+using {{packageName}}.{{modelPackage}};
+using {{packageName}}.{{apiName}}.Services;
+
+// Example `{{classname}}.{{operationId}}` usage:
+// Provide the following values: {{#allParams}}{{^isHeaderParam}}{{paramName}}{{^-last}}, {{/-last}}{{/isHeaderParam}}{{#isHeaderParam}}[HeaderParameter] {{paramName}}{{^-last}}, {{/-last}}{{/isHeaderParam}}{{/allParams}}
+{{#returnType}}{{^isVoid}}{{interfacePrefix}}{{returnType}} response = {{/isVoid}}{{/returnType}}await {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{operationId}}Async(
+ {{#pathParams}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/pathParams}}{{#queryParams}}{{#required}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/required}}{{/queryParams}}{{#bodyParams}}{{{dataType}}}{{>NullConditionalParameter}} {{paramName}}, {{/bodyParams}}{{#queryParams}}{{^required}}Option<{{{dataType}}}{{>NullConditionalParameter}}> {{paramName}}{{#notRequiredOrIsNullable}} = default{{/notRequiredOrIsNullable}}, {{/required}}{{/queryParams}}
+ RequestOptions requestOptions = default,
+ CancellationToken cancellationToken = default);
+
+if (response.TryDeserializeOkResponse(out {{returnType}} result))
+{
+ // Handle result, if 200 OK response
+}
+
+```
+
+#### Return type
+{{#returnType}}{{#returnTypeIsPrimitive}}[{{{returnType}}}]{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[{{returnType}}](){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}}
+
+
+{{/operation}}
+{{/operations}}
diff --git a/templates-v7/csharp/libraries/generichost/api_test.mustache b/templates-v7/csharp/libraries/generichost/api_test.mustache
new file mode 100644
index 000000000..d54244957
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/api_test.mustache
@@ -0,0 +1,71 @@
+{{>partial_header}}
+using {{packageName}}.Core.Options;{{#hasImport}}
+using {{packageName}}.{{modelPackage}};{{/hasImport}}
+using {{packageName}}.{{apiName}}.{{clientPackageName}};
+using {{packageName}}.{{apiName}}.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Text.Json;
+
+{{>testInstructions}}
+
+
+namespace {{packageName}}.Test.{{apiPackage}}
+{
+ ///
+ /// Class for testing {{classname}}
+ ///
+ [TestClass]
+ public sealed class {{classname}}Test
+ {
+ private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider;
+
+ public {{classname}}Test()
+ {
+ IHost host = Host.CreateDefaultBuilder()
+ .Configure{{classname}}((context, services, config) =>
+ {
+ config.ConfigureAdyenOptions(options =>
+ {
+ options.Environment = AdyenEnvironment.Test;
+ });
+ })
+ .Build();
+
+ _jsonSerializerOptionsProvider = host.Services.GetRequiredService();
+ }
+ {{#operations}}
+ {{#operation}}
+
+ ///
+ /// Test {{operationId}}.
+ ///
+ public async Task Given_Deserialize_When_{{operationId}}_Successfully_Deserializes()
+ {
+ // Arrange
+ // TODO: Insert your example code from open-api here
+ var client = TestUtilities.GetTestFileContent("mocks/posmobile/create-session.json");
+
+ {{#allParams}}
+ {{^required}}Client.Option<{{/required}}{{{dataType}}}{{>NullConditionalParameter}}{{^required}}>{{/required}} {{paramName}} = default{{nrt!}};
+ {{/allParams}}
+ {{#responses}}
+ {{#-first}}
+ {{#dataType}}
+
+ // Act
+ // TODO: Prefill the ResponseType
+ // IDEALLY: We insert the SERVICE and then we call it using the right parameters (this gets tricky with query params* but very doable!)
+ var response = JsonSerializer.Deserialize<{{name}}Response}>(json, _jsonSerializerOptionsProvider);
+
+ // Assert
+ Assert.IsNotNull(response);
+ {{/dataType}}
+ {{/-first}}
+ {{/responses}}
+ }
+ {{/operation}}
+ {{/operations}}
+ }
+}
diff --git a/templates-v7/csharp/libraries/generichost/model.mustache b/templates-v7/csharp/libraries/generichost/model.mustache
new file mode 100644
index 000000000..fa07a7053
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/model.mustache
@@ -0,0 +1,56 @@
+//
+{{>partial_header}}
+
+{{#nrt}}
+#nullable enable
+
+{{/nrt}}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.IO;
+{{^useGenericHost}}
+using System.Runtime.Serialization;
+{{/useGenericHost}}
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+{{#validatable}}
+using System.ComponentModel.DataAnnotations;
+{{/validatable}}
+{{#useCompareNetObjects}}
+using OpenAPIClientUtils = {{packageName}}.Client.ClientUtils;
+{{/useCompareNetObjects}}
+{{#useGenericHost}}
+{{#useSourceGeneration}}
+using System.Text.Json.Serialization.Metadata;
+{{/useSourceGeneration}}
+using {{packageName}}.{{corePackageName}};
+using {{packageName}}.{{apiName}}.{{clientPackage}};
+{{/useGenericHost}}
+{{#models}}
+{{#lambda.trimTrailingWithNewLine}}
+{{#model}}
+
+namespace {{packageName}}.{{modelPackage}}
+{
+{{#isEnum}}
+{{>modelEnum}}
+
+{{/isEnum}}
+{{^isEnum}}
+{{>modelGeneric}}
+
+
+{{>JsonConverter}}
+
+{{/isEnum}}
+{{>SourceGenerationContext}}
+
+{{/model}}
+{{/lambda.trimTrailingWithNewLine}}
+{{/models}}
+}
diff --git a/templates-v7/csharp/libraries/generichost/modelGeneric.mustache b/templates-v7/csharp/libraries/generichost/modelGeneric.mustache
new file mode 100644
index 000000000..00f340413
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/modelGeneric.mustache
@@ -0,0 +1,413 @@
+ ///
+ /// {{{description}}}{{^description}}{{classname}}{{/description}}.
+ ///
+ {{>visibility}} partial class {{classname}}{{#lambda.firstDot}}{{#parent}} : .{{/parent}}{{#validatable}} : .{{/validatable}}{{#equatable}}{{#readOnlyVars}}{{#-first}} : .{{/-first}}{{/readOnlyVars}}{{/equatable}}{{/lambda.firstDot}}{{#lambda.joinWithComma}}{{#parent}}{{{.}}} {{/parent}}{{>ImplementsIEquatable}}{{#validatable}}IValidatableObject {{/validatable}}{{/lambda.joinWithComma}}
+ {
+ {{#composedSchemas.oneOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ {{#composedSchemas.anyOf}}
+ ///
+ {{/composedSchemas.anyOf}}
+ {{#allVars}}
+ {{^isDiscriminator}}
+ /// {{description}}{{^description}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/description}}{{#defaultValue}} (default to {{.}}){{/defaultValue}}
+ {{/isDiscriminator}}
+ {{/allVars}}
+ {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}({{#lambda.joinWithComma}}{{{dataType}}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{#model.composedSchemas.anyOf}}{{^required}}Option<{{/required}}{{{dataType}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{baseType}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{/model.composedSchemas.anyOf}}{{>ModelSignature}}{{/lambda.joinWithComma}}){{#parent}} : base({{#lambda.joinWithComma}}{{#parentModel.composedSchemas.oneOf}}{{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{parent}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}.{{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}} {{/parentModel.composedSchemas.oneOf}}{{>ModelBaseSignature}}{{/lambda.joinWithComma}}){{/parent}}
+ {
+ {{#composedSchemas.anyOf}}
+ {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/composedSchemas.anyOf}}
+ {{name}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{#allVars}}
+ {{^isDiscriminator}}
+ {{^isInherited}}
+ {{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/isInherited}}
+ {{#isInherited}}
+ {{#isNew}}
+ {{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/isNew}}
+ {{/isInherited}}
+ {{/isDiscriminator}}
+ {{#vendorExtensions.x-is-base-or-new-discriminator}}
+ {{^model.composedSchemas.anyOf}}
+ {{^model.composedSchemas.oneOf}}
+ {{name}} = {{^isEnum}}this.GetType().Name{{/isEnum}}{{#isEnum}}({{datatypeWithEnum}})Enum.Parse(typeof({{datatypeWithEnum}}), this.GetType().Name){{/isEnum}};
+ {{/model.composedSchemas.oneOf}}
+ {{/model.composedSchemas.anyOf}}
+ {{/vendorExtensions.x-is-base-or-new-discriminator}}
+ {{/allVars}}
+ OnCreated();
+ }
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.oneOf}}
+ {{^composedSchemas.oneOf}}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ {{#composedSchemas.anyOf}}
+ ///
+ {{/composedSchemas.anyOf}}
+ {{#allVars}}
+ {{^isDiscriminator}}
+ /// {{description}}{{^description}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/description}}{{#defaultValue}} (default to {{.}}){{/defaultValue}}
+ {{/isDiscriminator}}
+ {{/allVars}}
+ {{^composedSchemas.anyOf}}
+ [JsonConstructor]
+ {{/composedSchemas.anyOf}}
+ {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}({{#lambda.joinWithComma}}{{#composedSchemas.anyOf}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{/composedSchemas.anyOf}}{{>ModelSignature}}{{/lambda.joinWithComma}}){{#parent}} : base({{#lambda.joinWithComma}}{{>ModelBaseSignature}}{{/lambda.joinWithComma}}){{/parent}}
+ {
+ {{#composedSchemas.anyOf}}
+ {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/composedSchemas.anyOf}}
+ {{#allVars}}
+ {{^isDiscriminator}}
+ {{^isInherited}}
+ {{^required}}_{{/required}}{{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/isInherited}}
+ {{#isInherited}}
+ {{#isNew}}
+ {{^required}}_{{/required}}{{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}};
+ {{/isNew}}
+ {{/isInherited}}
+ {{/isDiscriminator}}
+ {{#vendorExtensions.x-is-base-or-new-discriminator}}
+ {{^model.composedSchemas.anyOf}}
+ {{^model.composedSchemas.oneOf}}
+ {{name}} = {{^isEnum}}this.GetType().Name{{/isEnum}}{{#isEnum}}({{datatypeWithEnum}})Enum.Parse(typeof({{datatypeWithEnum}}), this.GetType().Name){{/isEnum}};
+ {{/model.composedSchemas.oneOf}}
+ {{/model.composedSchemas.anyOf}}
+ {{/vendorExtensions.x-is-base-or-new-discriminator}}
+ {{/allVars}}
+ OnCreated();
+ }
+
+ {{#allVars}}
+ {{^isDiscriminator}}
+ {{#-first}}
+ ///
+ /// Best practice: Use the constructor to initialize your objects to understand which parameters are required/optional.
+ ///
+ {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}()
+ {
+ }
+ {{/-first}}
+ {{/isDiscriminator}}
+ {{/allVars}}
+
+ {{/composedSchemas.oneOf}}
+ partial void OnCreated();
+
+ {{#vars}}
+ {{#items.isEnum}}
+ {{#items}}
+ {{^complexType}}
+{{>modelInnerEnum}}
+
+ {{/complexType}}
+ {{/items}}
+ {{/items.isEnum}}
+ {{#isEnum}}
+ {{^complexType}}
+{{>modelInnerEnum}}
+
+ {{/complexType}}
+ {{/isEnum}}
+ {{^isDiscriminator}}
+ {{#isEnum}}
+ {{^required}}
+ ///
+ /// This is used to track if an optional field is set. If set, will be populated.
+ ///
+ [JsonIgnore]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public {{#isNew}}new {{/isNew}}Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}}
+
+ {{/required}}
+ ///
+ /// {{{description}}}{{^description}}.{{/description}}
+ ///
+ {{#description}}
+ /// {{.}}
+ {{/description}}
+ {{#example}}
+ /* {{.}} */
+ {{/example}}
+ [JsonPropertyName("{{baseName}}")]
+ {{#deprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/deprecated}}
+ public {{#isNew}}new {{/isNew}}{{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option; } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}}
+
+ {{/isEnum}}
+ {{/isDiscriminator}}
+ {{/vars}}
+ {{#composedSchemas.anyOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ {{^required}}
+ ///
+ /// This is used to track if an optional field is set. If set, will be populated.
+ ///
+ [JsonIgnore]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public {{#isNew}}new {{/isNew}}Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}}
+
+ {{/required}}
+ ///
+ /// {{{description}}}{{^description}}.{{/description}}
+ /// {{#description}}
+ /// {{.}}{{/description}}
+ {{#example}}
+ /* {{.}} */
+ {{/example}}
+ {{#deprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/deprecated}}
+ public {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option; } {{^isReadOnly}}set { this._{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}}
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.anyOf}}
+ {{#composedSchemas.oneOf}}
+ {{^vendorExtensions.x-duplicated-data-type}}
+ ///
+ /// {{{description}}}{{^description}}.{{/description}}.
+ /// {{#description}}
+ /// {{.}}{{/description}}
+ {{#example}}
+ /* {{.}} */
+ {{/example}}
+ {{#deprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/deprecated}}
+ public {{{dataType}}}{{>NullConditionalProperty}} {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}} { get; {{^isReadOnly}}set; {{/isReadOnly}}}
+
+ {{/vendorExtensions.x-duplicated-data-type}}
+ {{/composedSchemas.oneOf}}
+ {{#allVars}}
+ {{#vendorExtensions.x-is-base-or-new-discriminator}}
+ {{^model.composedSchemas.anyOf}}
+ {{^model.composedSchemas.oneOf}}
+ ///
+ /// The discriminator.
+ ///
+ [JsonIgnore]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public {{#isNew}}new {{/isNew}}{{datatypeWithEnum}} {{name}} { get; }
+
+ {{/model.composedSchemas.oneOf}}
+ {{/model.composedSchemas.anyOf}}
+ {{/vendorExtensions.x-is-base-or-new-discriminator}}
+ {{^vendorExtensions.x-is-base-or-new-discriminator}}
+ {{^isEnum}}
+ {{#isInherited}}
+ {{#isNew}}
+ {{^required}}
+ ///
+ /// This is used to track if an optional field is set. If set, will be populated.
+ ///
+ [JsonIgnore]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public new Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}}
+
+ {{/required}}
+ ///
+ /// {{{description}}}{{^description}}.{{/description}}
+ /// {{#description}}
+ /// {{.}}{{/description}}
+ {{#example}}
+ /* {{.}} */
+ {{/example}}
+ [JsonPropertyName("{{baseName}}")]
+ {{#deprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/deprecated}}
+ public new {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}}
+
+ {{/isNew}}
+ {{/isInherited}}
+ {{^isInherited}}
+ {{^required}}
+ ///
+ /// This is used to track if an optional field is set. If set, will be populated.
+ ///
+ [JsonIgnore]
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ public Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}}
+
+ {{/required}}
+ ///
+ /// {{description}}{{^description}}.{{/description}}
+ /// {{#description}}
+ /// {{{description}}}{{/description}}
+ {{#example}}
+ /* {{.}} */
+ {{/example}}
+ [JsonPropertyName("{{baseName}}")]
+ {{#deprecated}}
+ [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
+ {{/deprecated}}
+ public {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option; } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}}
+
+ {{/isInherited}}
+ {{/isEnum}}
+ {{/vendorExtensions.x-is-base-or-new-discriminator}}
+ {{/allVars}}
+ {{#isAdditionalPropertiesTrue}}
+ {{^parentModel}}
+ ///
+ /// Gets or sets additional properties
+ ///
+ [JsonExtensionData]
+ public Dictionary AdditionalProperties { get; } = new Dictionary();
+
+ {{/parentModel}}
+ {{/isAdditionalPropertiesTrue}}
+ ///
+ /// Returns the string presentation of the object
+ ///
+ /// String presentation of the object
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("class {{classname}} {\n");
+ {{#composedSchemas.oneOf}}
+ if (this.{{name}} != null)
+ sb.Append({{name}}.ToString().Replace("\n", "\n "));
+ {{/composedSchemas.oneOf}}
+ {{#parent}}
+ sb.Append(" ").Append(base.ToString()?.Replace("\n", "\n ")).Append("\n");
+ {{/parent}}
+ {{#vars}}
+ {{^isDiscriminator}}
+ sb.Append(" {{name}}: ").Append({{name}}).Append("\n");
+ {{/isDiscriminator}}
+ {{/vars}}
+ {{#isAdditionalPropertiesTrue}}
+ {{^parentModel}}
+ sb.Append(" AdditionalProperties: ").Append(AdditionalProperties).Append("\n");
+ {{/parentModel}}
+ {{/isAdditionalPropertiesTrue}}
+ sb.Append("}\n");
+ return sb.ToString();
+ }
+ {{#equatable}}
+ {{#readOnlyVars}}
+ {{#-first}}
+
+ ///
+ /// Returns true if objects are equal.
+ ///
+ /// Object to be compared
+ /// Boolean
+ public override bool Equals(object{{nrt?}} input)
+ {
+ {{#useCompareNetObjects}}
+ return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual;
+ {{/useCompareNetObjects}}
+ {{^useCompareNetObjects}}
+ return this.Equals(input as {{classname}});
+ {{/useCompareNetObjects}}
+ }
+
+ ///
+ /// Returns true if {{classname}} instances are equal.
+ ///
+ /// Instance of {{classname}} to be compared
+ /// Boolean
+ public bool Equals({{classname}}{{nrt?}} input)
+ {
+ {{#useCompareNetObjects}}
+ return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual;
+ {{/useCompareNetObjects}}
+ {{^useCompareNetObjects}}
+ if (input == null)
+ return false;
+
+ return {{#parent}}base.Equals(input) && {{/parent}}{{#readOnlyVars}}{{^isInherited}}{{^isContainer}}
+ (
+ {{name}} == input.{{name}} ||
+ {{^vendorExtensions.x-is-value-type}}
+ ({{name}} != null &&
+ {{name}}.Equals(input.{{name}}))
+ {{/vendorExtensions.x-is-value-type}}
+ {{#vendorExtensions.x-is-value-type}}
+ {{name}}.Equals(input.{{name}})
+ {{/vendorExtensions.x-is-value-type}}
+ ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}}
+ (
+ {{name}} == input.{{name}} ||
+ {{^vendorExtensions.x-is-value-type}}{{name}} != null &&
+ input.{{name}} != null &&
+ {{/vendorExtensions.x-is-value-type}}{{name}}.SequenceEqual(input.{{name}})
+ ){{^-last}} && {{/-last}}{{/isContainer}}{{/isInherited}}{{/readOnlyVars}}{{^readOnlyVars}}{{#parent}}base.Equals(input){{/parent}}{{^parent}}false{{/parent}}{{/readOnlyVars}}{{^isAdditionalPropertiesTrue}};{{/isAdditionalPropertiesTrue}}
+ {{#isAdditionalPropertiesTrue}}
+ {{^parentModel}}
+ && (AdditionalProperties.Count == input.AdditionalProperties.Count && !AdditionalProperties.Except(input.AdditionalProperties).Any());
+ {{/parentModel}}
+ {{/isAdditionalPropertiesTrue}}
+ {{/useCompareNetObjects}}
+ }
+
+ ///
+ /// Gets the hash code.
+ ///
+ /// Hash code
+ public override int GetHashCode()
+ {
+ unchecked // Overflow is fine, just wrap
+ {
+ {{#parent}}
+ int hashCode = base.GetHashCode();
+ {{/parent}}
+ {{^parent}}
+ int hashCode = 41;
+ {{/parent}}
+ {{#readOnlyVars}}
+ {{#required}}
+ {{^isNullable}}
+ hashCode = (hashCode * 59) + {{name}}.GetHashCode();
+ {{/isNullable}}
+ {{/required}}
+ {{/readOnlyVars}}
+ {{#readOnlyVars}}
+ {{#lambda.copy}}
+ if ({{name}} != null)
+ hashCode = (hashCode * 59) + {{name}}.GetHashCode();
+
+ {{/lambda.copy}}
+ {{#isNullable}}
+ {{#lambda.pasteOnce}}
+ {{/lambda.pasteOnce}}
+ {{/isNullable}}
+ {{^required}}
+ {{#lambda.pasteOnce}}
+ {{/lambda.pasteOnce}}
+ {{/required}}
+ {{/readOnlyVars}}
+ {{#isAdditionalPropertiesTrue}}
+ {{^parentModel}}
+ hashCode = (hashCode * 59) + AdditionalProperties.GetHashCode();
+ {{/parentModel}}
+ {{/isAdditionalPropertiesTrue}}
+
+ return hashCode;
+ }
+ }
+ {{/-first}}
+ {{/readOnlyVars}}
+ {{/equatable}}
+{{#validatable}}
+{{^parentModel}}
+
+{{>validatable}}
+
+{{/parentModel}}
+{{/validatable}}
+ }
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache b/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache
new file mode 100644
index 000000000..572d0f136
--- /dev/null
+++ b/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache
@@ -0,0 +1,116 @@
+ {{^isContainer}}
+ ///
+ /// {{{description}}}{{^description}}Defines {{{name}}}.{{/description}}
+ ///
+ {{#description}}
+ /// {{.}}
+ {{/description}}
+ {{#isString}}
+ {{#useGenericHost}}
+ [JsonConverter(typeof({{datatypeWithEnum}}JsonConverter))]
+ {{/useGenericHost}}
+ {{/isString}}
+ {{>visibility}} class {{datatypeWithEnum}} : IEnum
+ {
+ ///
+ /// Returns the value of the {{datatypeWithEnum}}.
+ ///
+ public string? Value { get; set; }
+
+ {{#allowableValues}}
+ {{#enumVars}}
+ ///
+ /// {{datatypeWithEnum}}.{{name}} - {{value}}
+ ///
+ public static readonly {{datatypeWithEnum}} {{name}} = new("{{value}}");
+ {{^-last}}
+
+ {{/-last}}
+ {{/enumVars}}
+ {{/allowableValues}}
+
+ private {{datatypeWithEnum}}(string? value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Converts a string to a implicitly.
+ ///
+ /// The string value to convert. Defaults to null.
+ /// A new instance initialized with the string value.
+ public static implicit operator {{datatypeWithEnum}}?(string? value) => value == null ? null : new {{datatypeWithEnum}}(value);
+
+ ///
+ /// Converts a instance to a string implicitly.
+ ///
+ /// The instance. Default to null.
+ /// String value of the instance.///
+ public static implicit operator string?({{datatypeWithEnum}}? option) => option?.Value;
+
+ public static bool operator ==({{datatypeWithEnum}}? left, {{datatypeWithEnum}}? right) => string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase);
+
+ public static bool operator !=({{datatypeWithEnum}}? left, {{datatypeWithEnum}}? right) => !string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase);
+
+ public override bool Equals(object? obj) => obj is {{datatypeWithEnum}} other && string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase);
+
+ public override int GetHashCode() => Value?.GetHashCode() ?? 0;
+
+ public override string ToString() => Value ?? string.Empty;
+
+ {{#useGenericHost}}
+ ///
+ /// Returns a .
+ ///
+ ///
+ /// or null.
+ public static {{datatypeWithEnum}}? FromStringOrDefault(string value)
+ {
+ return value switch {
+ {{#allowableValues}}{{#enumVars}}{{#isString}}"{{{value}}}"{{/isString}} => {{datatypeWithEnum}}.{{name}},
+ {{/enumVars}}{{/allowableValues}}_ => null,
+ };
+ }
+
+ ///
+ /// Converts the to the json value.
+ ///
+ ///
+ /// String value of the enum.
+ {{#isString}}
+ ///
+ {{/isString}}
+ public static {{>EnumValueDataType}}?{{#lambda.first}}{{#nrt}}{{/nrt}}{{/lambda.first}} ToJsonValue({{datatypeWithEnum}}? value)
+ {
+ if (value == null)
+ return null;
+
+ {{#allowableValues}}
+ {{#enumVars}}
+ if (value == {{datatypeWithEnum}}.{{name}})
+ return {{#isString}}"{{{value}}}"{{/isString}};
+
+ {{/enumVars}}
+ {{/allowableValues}}
+ return null;
+ }
+
+ ///
+ /// JsonConverter for writing {{datatypeWithEnum}}.
+ ///
+ public class {{datatypeWithEnum}}JsonConverter : JsonConverter<{{datatypeWithEnum}}>
+ {
+ public override {{datatypeWithEnum}}? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions jsonOptions)
+ {
+ string value = reader.GetString();
+ return value == null ? null : {{datatypeWithEnum}}.FromStringOrDefault(value) ?? new {{datatypeWithEnum}}(value);
+ }
+
+ public override void Write(Utf8JsonWriter writer, {{datatypeWithEnum}} value, JsonSerializerOptions jsonOptions)
+ {
+ writer.WriteStringValue({{datatypeWithEnum}}.ToJsonValue(value));
+ }
+ }
+ {{/useGenericHost}}
+ }
+ {{/isContainer}}
\ No newline at end of file
diff --git a/templates-v7/csharp/partial_header.mustache b/templates-v7/csharp/partial_header.mustache
new file mode 100644
index 000000000..377ccfabb
--- /dev/null
+++ b/templates-v7/csharp/partial_header.mustache
@@ -0,0 +1,18 @@
+/*
+ {{#appName}}
+ * {{{.}}}
+ *
+ {{/appName}}
+ {{#appDescription}}
+ * {{{.}}}
+ *
+ {{/appDescription}}
+ {{#version}}
+ * The version of the OpenAPI document: {{{.}}}
+ {{/version}}
+ {{#infoEmail}}
+ * Contact: {{{.}}}
+ {{/infoEmail}}
+ * NOTE: This class is auto-generated using a modified version (https://github.com/Adyen/adyen-sdk-automation) of the OpenAPI Generator: https://github.com/openapitools/openapi-generator.git.
+ * Do not edit this class manually.
+ */
diff --git a/templates-v7/csharp/validatable.mustache b/templates-v7/csharp/validatable.mustache
new file mode 100644
index 000000000..14fbc18c4
--- /dev/null
+++ b/templates-v7/csharp/validatable.mustache
@@ -0,0 +1,141 @@
+{{#discriminator}}
+ ///
+ /// To validate all properties of the instance.
+ ///
+ /// .
+ /// .
+ IEnumerable IValidatableObject.Validate(ValidationContext validationContext)
+ {
+ return this.BaseValidate(validationContext);
+ }
+
+ ///
+ /// To validate all properties of the instance.
+ ///
+ /// .
+ /// .
+ protected IEnumerable BaseValidate(ValidationContext validationContext)
+ {
+{{/discriminator}}
+{{^discriminator}}
+ {{#parent}}
+ ///
+ /// To validate all properties of the instance.
+ ///
+ /// .
+ /// .
+ IEnumerable IValidatableObject.Validate(ValidationContext validationContext)
+ {
+ return this.BaseValidate(validationContext);
+ }
+
+ ///
+ /// To validate all properties of the instance.
+ ///
+ /// .
+ /// .
+ protected IEnumerable BaseValidate(ValidationContext validationContext)
+ {
+ {{/parent}}
+ {{^parent}}
+ ///
+ /// To validate all properties of the instance.
+ ///
+ /// .
+ /// .
+ IEnumerable IValidatableObject.Validate(ValidationContext validationContext)
+ {
+ {{/parent}}
+{{/discriminator}}
+ {{#parent}}
+ {{^isArray}}
+ {{^isMap}}
+ foreach (var x in {{#discriminator}}base.{{/discriminator}}BaseValidate(validationContext))
+ {
+ yield return x;
+ }
+ {{/isMap}}
+ {{/isArray}}
+ {{/parent}}
+ {{#vars}}
+ {{#hasValidation}}
+ {{^isEnum}}
+ {{^isDateTime}}
+ {{^isDate}}
+ {{^isTime}}
+ {{#maxLength}}
+ // {{{name}}} ({{{dataType}}}) maxLength
+ if (this.{{{name}}} != null && this.{{{name}}}.Length > {{maxLength}})
+ {
+ yield return new ValidationResult("Invalid value for {{{name}}}, length must be less than {{maxLength}}.", new [] { "{{{name}}}" });
+ }
+
+ {{/maxLength}}
+ {{#minLength}}
+ // {{{name}}} ({{{dataType}}}) minLength
+ if (this.{{{name}}} != null && this.{{{name}}}.Length < {{minLength}})
+ {
+ yield return new ValidationResult("Invalid value for {{{name}}}, length must be greater than {{minLength}}.", new [] { "{{{name}}}" });
+ }
+
+ {{/minLength}}
+ {{/isTime}}
+ {{/isDate}}
+ {{/isDateTime}}
+ {{#maximum}}
+ // {{{name}}} ({{{dataType}}}) maximum
+ if ({{#useGenericHost}}{{^required}}this.{{{name}}}Option.IsSet && {{/required}}{{/useGenericHost}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} {{#exclusiveMaximum}}<={{/exclusiveMaximum}}{{^exclusiveMaximum}}>{{/exclusiveMaximum}} ({{{dataType}}}){{maximum}})
+ {
+ yield return new ValidationResult("Invalid value for {{{name}}}, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.", new [] { "{{{name}}}" });
+ }
+
+ {{/maximum}}
+ {{#minimum}}
+ // {{{name}}} ({{{dataType}}}) minimum
+ if ({{#useGenericHost}}{{^required}}this.{{{name}}}Option.IsSet && {{/required}}{{/useGenericHost}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} {{#exclusiveMaximum}}>={{/exclusiveMaximum}}{{^exclusiveMaximum}}<{{/exclusiveMaximum}} ({{{dataType}}}){{minimum}})
+ {
+ yield return new ValidationResult("Invalid value for {{{name}}}, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.", new [] { "{{{name}}}" });
+ }
+
+ {{/minimum}}
+ {{#pattern}}
+ {{^isByteArray}}
+ {{#vendorExtensions.x-is-value-type}}
+ {{#isNullable}}
+ if (this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null){
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.indent4}}
+ {{>ValidateRegex}}{{! prevent indent}}
+ {{/lambda.indent4}}
+
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ {{/isNullable}}
+ {{^isNullable}}
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.indent3}}
+ {{>ValidateRegex}}{{! prevent indent}}
+ {{/lambda.indent3}}
+
+ {{/lambda.trimTrailingWithNewLine}}
+ {{/isNullable}}
+ {{/vendorExtensions.x-is-value-type}}
+ {{^vendorExtensions.x-is-value-type}}
+ if (this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null) {
+ {{#lambda.trimTrailingWithNewLine}}
+ {{#lambda.indent4}}
+ {{>ValidateRegex}}{{! prevent indent}}
+ {{/lambda.indent4}}
+
+ {{/lambda.trimTrailingWithNewLine}}
+ }
+
+ {{/vendorExtensions.x-is-value-type}}
+ {{/isByteArray}}
+ {{/pattern}}
+ {{/isEnum}}
+ {{/hasValidation}}
+ {{/vars}}
+ yield break;
+ }
\ No newline at end of file
diff --git a/templates-v7/csharp/visibility.mustache b/templates-v7/csharp/visibility.mustache
new file mode 100644
index 000000000..a1d1f4163
--- /dev/null
+++ b/templates-v7/csharp/visibility.mustache
@@ -0,0 +1 @@
+{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}}
\ No newline at end of file
diff --git a/templates/csharp/AbstractOpenAPISchema.mustache b/templates/csharp/AbstractOpenAPISchema.mustache
deleted file mode 100644
index ab90f174c..000000000
--- a/templates/csharp/AbstractOpenAPISchema.mustache
+++ /dev/null
@@ -1,71 +0,0 @@
-{{>partial_header}}
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Runtime.Serialization;
-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.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();
-
- // Check if the contains TypeEnum value
- protected static bool ContainsValue(string type) where T : struct, IConvertible
- {
- // Search for type in .TypeEnum
- List list = new List();
- var members = typeof(T).GetTypeInfo().DeclaredMembers;
- foreach (var member in members)
- {
- var val = member?.GetCustomAttribute(false)?.Value;
- if (!string.IsNullOrEmpty(val))
- {
- list.Add(val);
- }
- }
-
- return list.Contains(type);
- }
- }
-}
diff --git a/templates/csharp/api-single.mustache b/templates/csharp/api-single.mustache
deleted file mode 100644
index da60553d0..000000000
--- a/templates/csharp/api-single.mustache
+++ /dev/null
@@ -1,87 +0,0 @@
-{{>partial_header}}
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Adyen.Constants;
-using Adyen.Model;
-{{#hasImport}}using Adyen.{{modelPackage}};
-{{/hasImport}}
-
-namespace {{packageName}}.Service
-{
-{{#operations}}
- ///
- /// {{classname}} Interface
- ///
- public interface I{{customApi}}Service
- {
- {{#operation}}
-{{>method_documentation}}
- {{#returnType}}
- /// .
- {{/returnType}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}});
-
- {{#supportsAsync}}
-{{>method_documentation}}
- /// A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.{{#returnType}}
- /// Task of .{{/returnType}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}});
-
- {{/supportsAsync}}
- {{/operation}}
- }
- {{/operations}}
-
- {{#operations}}
- ///
- /// Represents a collection of functions to interact with the {{customApi}}Service API endpoints
- ///
- {{>visibility}} class {{customApi}}Service : AbstractService, I{{customApi}}Service
- {
- private readonly string _baseUrl;
-
- public {{customApi}}Service(Client client) : base(client)
- {
- _baseUrl = CreateBaseUrl("{{{basePath}}}");
- }
- {{#operation}}
-
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- public {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}})
- {
- {{#returnType}}return {{/returnType}}{{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_invoke}}).ConfigureAwait(false).GetAwaiter().GetResult();
- }
-
- {{#supportsAsync}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}public async Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}public async Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}})
- {
- {{#hasQueryParams}}
- // Build the query string
- var queryParams = new Dictionary();
- {{#queryParams}}
- {{^required}}if ({{paramName}} != null) {{/required}}queryParams.Add("{{baseName}}", {{paramName}}{{^isString}}{{^isDateTime}}.Value.ToString(){{/isDateTime}}{{#isDateTime}}.ToString("yyyy-MM-ddTHH:mm:ssZ"){{/isDateTime}}{{/isString}});
- {{/queryParams}}
- {{/hasQueryParams}}
- var endpoint = _baseUrl + {{#hasPathParams}}${{/hasPathParams}}"{{{path}}}"{{#hasQueryParams}} + ToQueryString(queryParams){{/hasQueryParams}};
- var resource = new ServiceResource(this, endpoint);
- {{#returnType}}return {{/returnType}}await resource.RequestAsync{{#returnType}}<{{modelPackage}}.{{returnType}}>{{/returnType}}({{#bodyParam}}{{paramName}}.ToJson(){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, requestOptions, new HttpMethod("{{httpMethod}}"), cancellationToken).ConfigureAwait(false);
- }
- {{/supportsAsync}}
- {{/operation}}
- }
- {{/operations}}
-}
\ No newline at end of file
diff --git a/templates/csharp/api.mustache b/templates/csharp/api.mustache
deleted file mode 100644
index 27ff0eb4d..000000000
--- a/templates/csharp/api.mustache
+++ /dev/null
@@ -1,86 +0,0 @@
-{{>partial_header}}
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Adyen.Model;
-{{#hasImport}}using Adyen.{{modelPackage}};
-{{/hasImport}}
-
-namespace {{packageName}}.{{apiPackage}}
-{
-{{#operations}}
- ///
- /// {{classname}} Interface
- ///
- public interface I{{classname}}
- {
- {{#operation}}
-{{>method_documentation}}
- {{#returnType}}
- /// .
- {{/returnType}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}});
-
- {{#supportsAsync}}
-{{>method_documentation}}
- /// A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.{{#returnType}}
- /// Task of .{{/returnType}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}});
-
- {{/supportsAsync}}
- {{/operation}}
- }
- {{/operations}}
-
- {{#operations}}
- ///
- /// Represents a collection of functions to interact with the {{classname}} API endpoints
- ///
- {{>visibility}} class {{classname}} : AbstractService, I{{classname}}
- {
- private readonly string _baseUrl;
-
- public {{classname}}(Client client) : base(client)
- {
- _baseUrl = CreateBaseUrl("{{{basePath}}}");
- }
- {{#operation}}
-
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- public {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}})
- {
- {{#returnType}}return {{/returnType}}{{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_invoke}}).ConfigureAwait(false).GetAwaiter().GetResult();
- }
-
- {{#supportsAsync}}
- {{#isDeprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/isDeprecated}}
- {{#returnType}}public async Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}public async Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}})
- {
- {{#hasQueryParams}}
- // Build the query string
- var queryParams = new Dictionary();
- {{#queryParams}}
- {{^required}}if ({{paramName}} != null) {{/required}}queryParams.Add("{{baseName}}", {{paramName}}{{^isString}}{{^isDateTime}}.ToString(){{/isDateTime}}{{#isDateTime}}{{^required}}.Value{{/required}}.ToString("yyyy-MM-ddTHH:mm:ssZ"){{/isDateTime}}{{/isString}});
- {{/queryParams}}
- {{/hasQueryParams}}
- var endpoint = _baseUrl + {{#hasPathParams}}${{/hasPathParams}}"{{{path}}}"{{#hasQueryParams}} + ToQueryString(queryParams){{/hasQueryParams}};
- var resource = new ServiceResource(this, endpoint);
- {{#returnType}}return {{/returnType}}await resource.RequestAsync{{#returnType}}<{{modelPackage}}.{{returnType}}>{{/returnType}}({{#bodyParam}}{{paramName}}.ToJson(){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, requestOptions, new HttpMethod("{{httpMethod}}"), cancellationToken).ConfigureAwait(false);
- }
- {{/supportsAsync}}
- {{/operation}}
- }
- {{/operations}}
-}
\ No newline at end of file
diff --git a/templates/csharp/api_invoke.mustache b/templates/csharp/api_invoke.mustache
deleted file mode 100644
index 45815fc53..000000000
--- a/templates/csharp/api_invoke.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{#requiredParams}}{{#isPathParam}}{{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/requiredParams}}{{#optionalParams}}{{#isPathParam}}{{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/optionalParams}}requestOptions
\ No newline at end of file
diff --git a/templates/csharp/api_parameter_ordering.mustache b/templates/csharp/api_parameter_ordering.mustache
deleted file mode 100644
index 593846f25..000000000
--- a/templates/csharp/api_parameter_ordering.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{#isPathParam}}{{{dataType}}} {{paramName}} = default, {{/isPathParam}}{{#isBodyParam}}{{{dataType}}} {{paramName}} = default, {{/isBodyParam}}{{#isQueryParam}}{{{dataType}}} {{paramName}} = default, {{/isQueryParam}}
\ No newline at end of file
diff --git a/templates/csharp/api_parameter_ordering_required.mustache b/templates/csharp/api_parameter_ordering_required.mustache
deleted file mode 100644
index 0c1ecfea1..000000000
--- a/templates/csharp/api_parameter_ordering_required.mustache
+++ /dev/null
@@ -1 +0,0 @@
-{{#isPathParam}}{{{dataType}}} {{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{{dataType}}} {{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}
\ No newline at end of file
diff --git a/templates/csharp/api_parameters.mustache b/templates/csharp/api_parameters.mustache
deleted file mode 100644
index 3ca742884..000000000
--- a/templates/csharp/api_parameters.mustache
+++ /dev/null
@@ -1,2 +0,0 @@
-{{! Path and body are required, followed by optional query string and request options }}
-{{#requiredParams}}{{>api_parameter_ordering_required}}{{/requiredParams}}{{#optionalParams}}{{>api_parameter_ordering}}{{/optionalParams}}RequestOptions requestOptions = default
\ No newline at end of file
diff --git a/templates/csharp/api_parameters_async.mustache b/templates/csharp/api_parameters_async.mustache
deleted file mode 100644
index 4287dc8b4..000000000
--- a/templates/csharp/api_parameters_async.mustache
+++ /dev/null
@@ -1,2 +0,0 @@
-{{! Path and body are required, followed by optional query string and request options }}
-{{#requiredParams}}{{>api_parameter_ordering_required}}{{/requiredParams}}{{#optionalParams}}{{>api_parameter_ordering}}{{/optionalParams}}RequestOptions requestOptions = default, CancellationToken cancellationToken = default
\ No newline at end of file
diff --git a/templates/csharp/config.yaml b/templates/csharp/config.yaml
deleted file mode 100644
index 829b2714b..000000000
--- a/templates/csharp/config.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-templateDir: ./templates/csharp
-files:
- api-single.mustache:
- folder: api
- templateType: API
- destinationFilename: Single.cs
\ No newline at end of file
diff --git a/templates/csharp/method_documentation.mustache b/templates/csharp/method_documentation.mustache
deleted file mode 100644
index 537b3924e..000000000
--- a/templates/csharp/method_documentation.mustache
+++ /dev/null
@@ -1,13 +0,0 @@
- ///
- /// {{{summary}}}
- ///
- {{#pathParams}}
- /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
- {{/pathParams}}
- {{#bodyParams}}
- /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
- {{/bodyParams}}
- {{#queryParams}}
- /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
- {{/queryParams}}
- /// - Additional request options.
\ No newline at end of file
diff --git a/templates/csharp/model.mustache b/templates/csharp/model.mustache
deleted file mode 100644
index 51a2ad036..000000000
--- a/templates/csharp/model.mustache
+++ /dev/null
@@ -1,49 +0,0 @@
-{{>partial_header}}
-{{#models}}
-{{#model}}
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.IO;
-{{#vendorExtensions.x-com-visible}}
-using System.Runtime.InteropServices;
-{{/vendorExtensions.x-com-visible}}
-using System.Runtime.Serialization;
-using System.Text;
-using System.Text.RegularExpressions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
-using Newtonsoft.Json.Linq;
-{{#discriminator}}
-using JsonSubTypes;
-{{/discriminator}}
-{{/model}}
-{{/models}}
-{{#validatable}}
-using System.ComponentModel.DataAnnotations;
-{{/validatable}}
-using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter;
-{{#useCompareNetObjects}}
-using OpenAPIClientUtils = {{packageName}}.Client.ClientUtils;
-{{/useCompareNetObjects}}
-{{#models}}
-{{#model}}
-{{#oneOf}}
-{{#-first}}
-using System.Reflection;
-{{/-first}}
-{{/oneOf}}
-{{#anyOf}}
-{{#-first}}
-using System.Reflection;
-{{/-first}}
-{{/anyOf}}
-
-namespace {{packageName}}.{{modelPackage}}
-{
-{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{#anyOf}}{{#-first}}{{>modelAnyOf}}{{/-first}}{{/anyOf}}{{^oneOf}}{{^anyOf}}{{>modelGeneric}}{{/anyOf}}{{/oneOf}}{{/isEnum}}
-{{/model}}
-{{/models}}
-}
diff --git a/templates/csharp/modelGeneric.mustache b/templates/csharp/modelGeneric.mustache
deleted file mode 100644
index ffeedcb6e..000000000
--- a/templates/csharp/modelGeneric.mustache
+++ /dev/null
@@ -1,384 +0,0 @@
- ///
- /// {{description}}{{^description}}{{classname}}{{/description}}
- ///
- {{#vendorExtensions.x-cls-compliant}}
- [CLSCompliant({{{vendorExtensions.x-cls-compliant}}})]
- {{/vendorExtensions.x-cls-compliant}}
- {{#vendorExtensions.x-com-visible}}
- [ComVisible({{{vendorExtensions.x-com-visible}}})]
- {{/vendorExtensions.x-com-visible}}
- [DataContract(Name = "{{{name}}}")]
- {{#discriminator}}
- [JsonConverter(typeof(JsonSubtypes), "{{{discriminatorName}}}")]
- {{#mappedModels}}
- [JsonSubtypes.KnownSubType(typeof({{{modelName}}}), "{{^vendorExtensions.x-discriminator-value}}{{{mappingName}}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{.}}}{{/vendorExtensions.x-discriminator-value}}")]
- {{/mappedModels}}
- {{/discriminator}}
- {{>visibility}} partial class {{classname}} : {{#parent}}{{{.}}}, {{/parent}}IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}}
- {
- {{#vars}}
- {{#items.isEnum}}
- {{#items}}
- {{^complexType}}
-{{>modelInnerEnum}}
- {{/complexType}}
- {{/items}}
- {{/items.isEnum}}
- {{#isEnum}}
- {{^complexType}}
-{{>modelInnerEnum}}
- {{/complexType}}
- {{/isEnum}}
- {{#isEnum}}
-
- ///
- /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
- ///
- {{#description}}
- /// {{.}}
- {{/description}}
- {{^conditionalSerialization}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} { get; set; }
- {{#isReadOnly}}
-
- ///
- /// Returns false as {{name}} should not be serialized given that it's read-only.
- ///
- /// false (boolean)
- public bool ShouldSerialize{{name}}()
- {
- return false;
- }
- {{/isReadOnly}}
- {{/conditionalSerialization}}
- {{#conditionalSerialization}}
- {{#isReadOnly}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} { get; set; }
-
- ///
- /// Returns false as {{name}} should not be serialized given that it's read-only.
- ///
- /// false (boolean)
- public bool ShouldSerialize{{name}}()
- {
- return false;
- }
- {{/isReadOnly}}
-
- {{^isReadOnly}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}true{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}}
- {
- get{ return _{{name}};}
- set
- {
- _{{name}} = value;
- _flag{{name}} = true;
- }
- }
- private {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} _{{name}};
- private bool _flag{{name}};
-
- ///
- /// Returns false as {{name}} should not be serialized given that it's read-only.
- ///
- /// false (boolean)
- public bool ShouldSerialize{{name}}()
- {
- return _flag{{name}};
- }
- {{/isReadOnly}}
- {{/conditionalSerialization}}
- {{/isEnum}}
- {{/vars}}
- {{#hasRequired}}
- {{^hasOnlyReadOnly}}
- ///
- /// Initializes a new instance of the class.
- ///
- [JsonConstructorAttribute]
- {{^isAdditionalPropertiesTrue}}
- protected {{classname}}() { }
- {{/isAdditionalPropertiesTrue}}
- {{#isAdditionalPropertiesTrue}}
- protected {{classname}}()
- {
- this.AdditionalProperties = new Dictionary();
- }
- {{/isAdditionalPropertiesTrue}}
- {{/hasOnlyReadOnly}}
- {{/hasRequired}}
- ///
- /// Initializes a new instance of the class.
- ///
- {{#readWriteVars}}
- /// {{description}}{{^description}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{/description}}{{#required}} (required){{/required}}{{#defaultValue}} (default to {{.}}){{/defaultValue}}.
- {{/readWriteVars}}
- {{#hasOnlyReadOnly}}
- [JsonConstructorAttribute]
- {{/hasOnlyReadOnly}}
- public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}} {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} = {{#defaultValue}}{{^isDateTime}}{{{defaultValue}}}{{/isDateTime}}{{#isDateTime}}default({{{datatypeWithEnum}}}){{/isDateTime}}{{/defaultValue}}{{^defaultValue}}default({{{datatypeWithEnum}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}}){{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}){{#parent}} : base({{#parentVars}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{^-last}}, {{/-last}}{{/parentVars}}){{/parent}}
- {
- {{#vars}}
- {{^isInherited}}
- {{^isReadOnly}}
- {{#required}}
- {{^conditionalSerialization}}
- {{^vendorExtensions.x-csharp-value-type}}
- this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{#vendorExtensions.x-csharp-value-type}}
- this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{/conditionalSerialization}}
- {{#conditionalSerialization}}
- {{^vendorExtensions.x-csharp-value-type}}
- this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{#vendorExtensions.x-csharp-value-type}}
- this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{/conditionalSerialization}}
- {{/required}}
- {{/isReadOnly}}
- {{/isInherited}}
- {{/vars}}
- {{#vars}}
- {{^isInherited}}
- {{^isReadOnly}}
- {{^required}}
- {{#defaultValue}}
- {{^conditionalSerialization}}
- {{^vendorExtensions.x-csharp-value-type}}
- // use default value if no "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" provided
- this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} ?? {{{defaultValue}}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{#vendorExtensions.x-csharp-value-type}}
- this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/vendorExtensions.x-csharp-value-type}}
- {{/conditionalSerialization}}
- {{/defaultValue}}
- {{^defaultValue}}
- {{^conditionalSerialization}}
- this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- {{/conditionalSerialization}}
- {{#conditionalSerialization}}
- this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}};
- if (this.{{name}} != null)
- {
- this._flag{{name}} = true;
- }
- {{/conditionalSerialization}}
- {{/defaultValue}}
- {{/required}}
- {{/isReadOnly}}
- {{/isInherited}}
- {{/vars}}
- {{#isAdditionalPropertiesTrue}}
- this.AdditionalProperties = new Dictionary();
- {{/isAdditionalPropertiesTrue}}
- }
-
- {{#vars}}
- {{^isInherited}}
- {{^isEnum}}
- ///
- /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
- /// {{#description}}
- /// {{.}}{{/description}}
- {{^conditionalSerialization}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#isDate}}
- [JsonConverter(typeof(OpenAPIDateConverter))]
- {{/isDate}}
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{dataType}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; }
-
- {{/conditionalSerialization}}
- {{#conditionalSerialization}}
- {{#isReadOnly}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}true{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#isDate}}
- [JsonConverter(typeof(OpenAPIDateConverter))]
- {{/isDate}}
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{dataType}}} {{name}} { get; private set; }
-
- {{/isReadOnly}}
- {{^isReadOnly}}
- {{#isDate}}
- [JsonConverter(typeof(OpenAPIDateConverter))]
- {{/isDate}}
- [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})]
- {{#deprecated}}
- [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")]
- {{/deprecated}}
- public {{{dataType}}} {{name}}
- {
- get{ return _{{name}};}
- set
- {
- _{{name}} = value;
- _flag{{name}} = true;
- }
- }
- private {{{dataType}}} _{{name}};
- private bool _flag{{name}};
-
- ///
- /// Returns false as {{name}} should not be serialized given that it's read-only.
- ///
- /// false (boolean)
- public bool ShouldSerialize{{name}}()
- {
- return _flag{{name}};
- }
- {{/isReadOnly}}
- {{/conditionalSerialization}}
- {{/isEnum}}
- {{/isInherited}}
- {{/vars}}
- {{#isAdditionalPropertiesTrue}}
- ///
- /// Gets or Sets additional properties
- ///
- [JsonExtensionData]
- public IDictionary AdditionalProperties { get; set; }
-
- {{/isAdditionalPropertiesTrue}}
- ///
- /// Returns the string presentation of the object
- ///
- /// String presentation of the object
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder();
- sb.Append("class {{classname}} {\n");
- {{#parent}}
- sb.Append(" ").Append(base.ToString().Replace("\n", "\n ")).Append("\n");
- {{/parent}}
- {{#vars}}
- sb.Append(" {{name}}: ").Append({{name}}).Append("\n");
- {{/vars}}
- {{#isAdditionalPropertiesTrue}}
- sb.Append(" AdditionalProperties: ").Append(AdditionalProperties).Append("\n");
- {{/isAdditionalPropertiesTrue}}
- sb.Append("}\n");
- return sb.ToString();
- }
-
- ///
- /// Returns the JSON string presentation of the object
- ///
- /// JSON string presentation of the object
- public {{#parent}}{{^isArray}}{{^isMap}}override {{/isMap}}{{/isArray}}{{/parent}}{{^parent}}virtual {{/parent}}string ToJson()
- {
- return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented);
- }
-
- ///
- /// Returns true if objects are equal
- ///
- /// Object to be compared
- /// Boolean
- public override bool Equals(object input)
- {
- {{#useCompareNetObjects}}
- return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual;
- {{/useCompareNetObjects}}
- {{^useCompareNetObjects}}
- return this.Equals(input as {{classname}});
- {{/useCompareNetObjects}}
- }
-
- ///
- /// Returns true if {{classname}} instances are equal
- ///
- /// Instance of {{classname}} to be compared
- /// Boolean
- public bool Equals({{classname}} input)
- {
- {{#useCompareNetObjects}}
- return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual;
- {{/useCompareNetObjects}}
- {{^useCompareNetObjects}}
- if (input == null)
- {
- return false;
- }
- return {{#vars}}{{#parent}}base.Equals(input) && {{/parent}}{{^isContainer}}
- (
- this.{{name}} == input.{{name}} ||
- {{^vendorExtensions.x-is-value-type}}
- (this.{{name}} != null &&
- this.{{name}}.Equals(input.{{name}}))
- {{/vendorExtensions.x-is-value-type}}
- {{#vendorExtensions.x-is-value-type}}
- this.{{name}}.Equals(input.{{name}})
- {{/vendorExtensions.x-is-value-type}}
- ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}}
- (
- this.{{name}} == input.{{name}} ||
- {{^vendorExtensions.x-is-value-type}}this.{{name}} != null &&
- input.{{name}} != null &&
- {{/vendorExtensions.x-is-value-type}}this.{{name}}.SequenceEqual(input.{{name}})
- ){{^-last}} && {{/-last}}{{/isContainer}}{{/vars}}{{^vars}}{{#parent}}base.Equals(input){{/parent}}{{^parent}}false{{/parent}}{{/vars}}{{^isAdditionalPropertiesTrue}};{{/isAdditionalPropertiesTrue}}
- {{#isAdditionalPropertiesTrue}}
- && (this.AdditionalProperties.Count == input.AdditionalProperties.Count && !this.AdditionalProperties.Except(input.AdditionalProperties).Any());
- {{/isAdditionalPropertiesTrue}}
- {{/useCompareNetObjects}}
- }
-
- ///
- /// Gets the hash code
- ///
- /// Hash code
- public override int GetHashCode()
- {
- unchecked // Overflow is fine, just wrap
- {
- {{#parent}}
- int hashCode = base.GetHashCode();
- {{/parent}}
- {{^parent}}
- int hashCode = 41;
- {{/parent}}
- {{#vars}}
- {{^vendorExtensions.x-is-value-type}}
- if (this.{{name}} != null)
- {
- hashCode = (hashCode * 59) + this.{{name}}.GetHashCode();
- }
- {{/vendorExtensions.x-is-value-type}}
- {{#vendorExtensions.x-is-value-type}}
- hashCode = (hashCode * 59) + this.{{name}}.GetHashCode();
- {{/vendorExtensions.x-is-value-type}}
- {{/vars}}
- {{#isAdditionalPropertiesTrue}}
- if (this.AdditionalProperties != null)
- {
- hashCode = (hashCode * 59) + this.AdditionalProperties.GetHashCode();
- }
- {{/isAdditionalPropertiesTrue}}
- return hashCode;
- }
- }
-{{#validatable}}
-{{>validatable}}
-{{/validatable}}
- }
diff --git a/templates/csharp/modelOneOf.mustache b/templates/csharp/modelOneOf.mustache
deleted file mode 100644
index 66721caf6..000000000
--- a/templates/csharp/modelOneOf.mustache
+++ /dev/null
@@ -1,286 +0,0 @@
-{{#model}}
- ///
- /// {{description}}{{^description}}{{classname}}{{/description}}
- ///
- {{#vendorExtensions.x-cls-compliant}}
- [CLSCompliant({{{.}}})]
- {{/vendorExtensions.x-cls-compliant}}
- {{#vendorExtensions.x-com-visible}}
- [ComVisible({{{.}}})]
- {{/vendorExtensions.x-com-visible}}
- [JsonConverter(typeof({{classname}}JsonConverter))]
- [DataContract(Name = "{{{name}}}")]
- {{>visibility}} partial class {{classname}} : AbstractOpenAPISchema, {{#parent}}{{{.}}}, {{/parent}}IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}}
- {
- {{#isNullable}}
- ///
- /// Initializes a new instance of the class.
- ///
- public {{classname}}()
- {
- this.IsNullable = true;
- this.SchemaType= "oneOf";
- }
-
- {{/isNullable}}
- {{#composedSchemas.oneOf}}
- {{^isNull}}
- ///
- /// Initializes a new instance of the class
- /// with the class
- ///
- /// An instance of {{dataType}}.
- public {{classname}}({{{dataType}}} actualInstance)
- {
- this.IsNullable = {{#model.isNullable}}true{{/model.isNullable}}{{^model.isNullable}}false{{/model.isNullable}};
- this.SchemaType= "oneOf";
- this.ActualInstance = actualInstance{{^model.isNullable}}{{^isPrimitiveType}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isPrimitiveType}}{{#isPrimitiveType}}{{#isArray}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isArray}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isFreeFormObject}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isFreeFormObject}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isString}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isString}}{{/isPrimitiveType}}{{/model.isNullable}};
- }
-
- {{/isNull}}
- {{/composedSchemas.oneOf}}
-
- private Object _actualInstance;
-
- ///
- /// Gets or Sets ActualInstance
- ///
- public override Object ActualInstance
- {
- get
- {
- return _actualInstance;
- }
- set
- {
- {{#oneOf}}
- {{^-first}}else {{/-first}}if (value.GetType() == typeof({{{.}}}))
- {
- this._actualInstance = value;
- }
- {{/oneOf}}
- else
- {
- throw new ArgumentException("Invalid instance found. Must be the following types:{{#oneOf}} {{{.}}}{{^-last}},{{/-last}}{{/oneOf}}");
- }
- }
- }
- {{#composedSchemas.oneOf}}
- {{^isNull}}
-
- ///
- /// Get the actual instance of `{{dataType}}`. If the actual instance is not `{{dataType}}`,
- /// the InvalidClassException will be thrown
- ///
- /// An instance of {{dataType}}
- public {{{dataType}}} Get{{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}}{{#isArray}}{{#lambda.titlecase}}{{{dataFormat}}}{{/lambda.titlecase}}{{/isArray}}()
- {
- return ({{{dataType}}})this.ActualInstance;
- }
- {{/isNull}}
- {{/composedSchemas.oneOf}}
-
- ///
- /// Returns the string presentation of the object
- ///
- /// String presentation of the object
- public override string ToString()
- {
- var sb = new StringBuilder();
- sb.Append("class {{classname}} {\n");
- sb.Append(" ActualInstance: ").Append(this.ActualInstance).Append("\n");
- sb.Append("}\n");
- return sb.ToString();
- }
-
- ///
- /// Returns the JSON string presentation of the object
- ///
- /// JSON string presentation of the object
- public override string ToJson()
- {
- return JsonConvert.SerializeObject(this.ActualInstance, {{classname}}.SerializerSettings);
- }
-
- ///
- /// Converts the JSON string into an instance of {{classname}}
- ///
- /// JSON string
- /// An instance of {{classname}}
- public static {{classname}} FromJson(string jsonString)
- {
- {{classname}} new{{classname}} = null;
-
- if (string.IsNullOrEmpty(jsonString))
- {
- return new{{classname}};
- }
- {{#useOneOfDiscriminatorLookup}}
- {{#discriminator}}
-
- try
- {
- var discriminatorObj = JObject.Parse(jsonString)["{{{propertyBaseName}}}"];
- string discriminatorValue = discriminatorObj == null ?string.Empty :discriminatorObj.ToString();
- switch (discriminatorValue)
- {
- {{#mappedModels}}
- case "{{{mappingName}}}":
- new{{classname}} = new {{classname}}(JsonConvert.DeserializeObject<{{{modelName}}}>(jsonString, {{classname}}.AdditionalPropertiesSerializerSettings));
- return new{{classname}};
- {{/mappedModels}}
- default:
- System.Diagnostics.Debug.WriteLine(string.Format("Failed to lookup discriminator value `{0}` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", discriminatorValue));
- break;
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(string.Format("Failed to parse the json data : `{0}` {1}", jsonString, ex.ToString()));
- }
-
- {{/discriminator}}
- {{/useOneOfDiscriminatorLookup}}
- int match = 0;
- List matchedTypes = new List();
- JToken typeToken = JObject.Parse(jsonString).GetValue("type");
- string type = typeToken?.Value();
- // Throw exception if jsonString does not contain type param
- if (type == null)
- {
- throw new InvalidDataException("JsonString does not contain required enum type for deserialization.");
- }
- try
- {
- {{#oneOf}}
- // Check if the jsonString type enum matches the {{{.}}} type enums
- if (ContainsValue<{{{.}}}.TypeEnum>(type))
- {
- new{{classname}} = new {{classname}}(JsonConvert.DeserializeObject<{{{.}}}>(jsonString, {{classname}}.SerializerSettings));
- matchedTypes.Add("{{{.}}}");
- match++;
- }
- {{/oneOf}}
- }
- catch (Exception ex)
- {
- if (!(ex is JsonSerializationException))
- {
- throw new InvalidDataException(string.Format("Failed to deserialize `{0}` into target: {1}", jsonString, ex.ToString()));
- }
- }
-
- if (match != 1)
- {
- throw new InvalidDataException("The JSON string `" + jsonString + "` cannot be deserialized into any schema defined. MatchedTypes are: " + matchedTypes);
- }
-
- // deserialization is considered successful at this point if no exception has been thrown.
- return new{{classname}};
- }
-
- ///
- /// Returns true if objects are equal
- ///
- /// Object to be compared
- /// Boolean
- public override bool Equals(object input)
- {
- {{#useCompareNetObjects}}
- return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual;
- {{/useCompareNetObjects}}
- {{^useCompareNetObjects}}
- return this.Equals(input as {{classname}});
- {{/useCompareNetObjects}}
- }
-
- ///
- /// Returns true if {{classname}} instances are equal
- ///
- /// Instance of {{classname}} to be compared
- /// Boolean
- public bool Equals({{classname}} input)
- {
- {{#useCompareNetObjects}}
- return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual;
- {{/useCompareNetObjects}}
- {{^useCompareNetObjects}}
- if (input == null)
- return false;
-
- return this.ActualInstance.Equals(input.ActualInstance);
- {{/useCompareNetObjects}}
- }
-
- ///
- /// Gets the hash code
- ///
- /// Hash code
- public override int GetHashCode()
- {
- unchecked // Overflow is fine, just wrap
- {
- int hashCode = 41;
- if (this.ActualInstance != null)
- hashCode = hashCode * 59 + this.ActualInstance.GetHashCode();
- return hashCode;
- }
- }
-
- {{#validatable}}
- ///
- /// To validate all properties of the instance
- ///
- /// Validation context
- /// Validation Result
- IEnumerable IValidatableObject.Validate(ValidationContext validationContext)
- {
- yield break;
- }
- {{/validatable}}
- }
-
- ///
- /// Custom JSON converter for {{classname}}
- ///
- public class {{classname}}JsonConverter : JsonConverter
- {
- ///
- /// To write the JSON string
- ///
- /// JSON writer
- /// Object to be converted into a JSON string
- /// JSON Serializer
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- writer.WriteRawValue((string)(typeof({{classname}}).GetMethod("ToJson").Invoke(value, null)));
- }
-
- ///
- /// To convert a JSON string into an object
- ///
- /// JSON reader
- /// Object type
- /// Existing value
- /// JSON Serializer
- /// The object converted from the JSON string
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if(reader.TokenType != JsonToken.Null)
- {
- return {{classname}}.FromJson(JObject.Load(reader).ToString(Formatting.None));
- }
- return null;
- }
-
- ///
- /// Check if the object can be converted
- ///
- /// Object type
- /// True if the object can be converted
- public override bool CanConvert(Type objectType)
- {
- return false;
- }
- }
-{{/model}}
diff --git a/templates/csharp/partial_header.mustache b/templates/csharp/partial_header.mustache
deleted file mode 100644
index a4c7dd929..000000000
--- a/templates/csharp/partial_header.mustache
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-{{#appName}}
-* {{{.}}}
-*
-{{/appName}}
-*
-* {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}}
-*
-* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-* https://openapi-generator.tech
-* Do not edit the class manually.
-*/
From 33998d6ac7d7724c6c9700378d5f2b43249bbba7 Mon Sep 17 00:00:00 2001
From: Kwok He <105217051+Kwok-he-Chu@users.noreply.github.com>
Date: Fri, 28 Nov 2025 13:46:05 +0100
Subject: [PATCH 4/4] Remove HttpSigningConfiguration and HttpSigningToken
mustache templates
---
.../generichost/ApiTestsBase.mustache | 66 --
.../DependencyInjectionTests.mustache | 211 ------
.../HttpSigningConfiguration.mustache | 679 ------------------
.../generichost/HttpSigningToken.mustache | 45 --
.../generichost/README.client.mustache | 2 +-
.../csharp/libraries/generichost/api.mustache | 23 +-
6 files changed, 4 insertions(+), 1022 deletions(-)
delete mode 100644 templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
delete mode 100644 templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
delete mode 100644 templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
delete mode 100644 templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
diff --git a/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache b/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
deleted file mode 100644
index 789de490e..000000000
--- a/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache
+++ /dev/null
@@ -1,66 +0,0 @@
-{{>partial_header}}
-using System;
-using System.Collections.Generic;
-using System.Security.Cryptography;
-using Microsoft.Extensions.Hosting;
-using {{packageName}}.{{clientPackage}};{{#hasImport}}
-using {{packageName}}.{{modelPackage}};{{/hasImport}}
-using {{packageName}}.Extensions;
-
-
-{{>testInstructions}}
-
-
-
-namespace {{packageName}}.Test.{{apiPackage}}
-{
- ///
- /// Base class for API tests
- ///
- public class ApiTestsBase
- {
- protected readonly IHost _host;
-
- public ApiTestsBase(string[] args)
- {
- _host = CreateHostBuilder(args).Build();
- }
-
- public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)
- .Configure{{apiName}}((context, services, options) =>
- {
- {{#lambda.trimTrailingWithNewLine}}
- {{#apiKeyMethods}}
- string apiKeyTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
- ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}(apiKeyTokenValue{{-index}}, ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(apiKeyToken{{-index}});
-
- {{/apiKeyMethods}}
- {{#httpBearerMethods}}
- string bearerTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
- BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}(bearerTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(bearerToken{{-index}});
-
- {{/httpBearerMethods}}
- {{#httpBasicMethods}}
- string basicTokenUsername{{-index}} = context.Configuration[""] ?? throw new Exception("Username not found.");
- string basicTokenPassword{{-index}} = context.Configuration[""] ?? throw new Exception("Password not found.");
- BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}(basicTokenUsername{{-index}}, basicTokenPassword{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(basicToken{{-index}});
-
- {{/httpBasicMethods}}
- {{#httpSignatureMethods}}
- HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, new List(), HashAlgorithmName.SHA256, "", 0);
- HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(httpSignatureToken{{-index}});
-
- {{/httpSignatureMethods}}
- {{#oauthMethods}}
- string oauthTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found.");
- OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}(oauthTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(oauthToken{{-index}});
- {{/oauthMethods}}
- {{/lambda.trimTrailingWithNewLine}}
- });
- }
-}
diff --git a/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache b/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
deleted file mode 100644
index 6085b51e5..000000000
--- a/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache
+++ /dev/null
@@ -1,211 +0,0 @@
-{{>partial_header}}
-using System;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.DependencyInjection;
-using System.Collections.Generic;
-using System.Security.Cryptography;
-using {{packageName}}.{{clientPackage}};
-using {{packageName}}.{{apiPackage}};
-using {{packageName}}.Extensions;
-using Xunit;
-
-namespace {{packageName}}.Test.{{apiPackage}}
-{
- ///
- /// Tests the dependency injection.
- ///
- public class DependencyInjectionTest
- {
- private readonly IHost _hostUsingConfigureWithoutAClient =
- Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) =>
- {
- {{#lambda.trimTrailingWithNewLine}}
- {{#apiKeyMethods}}
- ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(apiKeyToken{{-index}});
-
- {{/apiKeyMethods}}
- {{#httpBearerMethods}}
- BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(bearerToken{{-index}});
-
- {{/httpBearerMethods}}
- {{#httpBasicMethods}}
- BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(basicToken{{-index}});
-
- {{/httpBasicMethods}}
- {{#httpSignatureMethods}}
- HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
- HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(httpSignatureToken{{-index}});
-
- {{/httpSignatureMethods}}
- {{#oauthMethods}}
- OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(oauthToken{{-index}});
-
- {{/oauthMethods}}
- {{/lambda.trimTrailingWithNewLine}}
- })
- .Build();
-
- private readonly IHost _hostUsingConfigureWithAClient =
- Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) =>
- {
- {{#lambda.trimTrailingWithNewLine}}
- {{#apiKeyMethods}}
- ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(apiKeyToken{{-index}});
-
- {{/apiKeyMethods}}
- {{#httpBearerMethods}}
- BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(bearerToken{{-index}});
-
- {{/httpBearerMethods}}
- {{#httpBasicMethods}}
- BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(basicToken{{-index}});
-
- {{/httpBasicMethods}}
- {{#httpSignatureMethods}}
- HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
- HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(httpSignatureToken{{-index}});
-
- {{/httpSignatureMethods}}
- {{#oauthMethods}}
- OAuthToken oauthToken = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(oauthToken);
-
- {{/oauthMethods}}
- {{/lambda.trimTrailingWithNewLine}}
- options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS));
- })
- .Build();
-
- private readonly IHost _hostUsingAddWithoutAClient =
- Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) =>
- {
- services.Add{{apiName}}(options =>
- {
- {{#lambda.trimTrailingWithNewLine}}
- {{#apiKeyMethods}}
- ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(apiKeyToken{{-index}});
-
- {{/apiKeyMethods}}
- {{#httpBearerMethods}}
- BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(bearerToken{{-index}});
-
- {{/httpBearerMethods}}
- {{#httpBasicMethods}}
- BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(basicToken{{-index}});
-
- {{/httpBasicMethods}}
- {{#httpSignatureMethods}}
- HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
- HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(httpSignatureToken{{-index}});
-
- {{/httpSignatureMethods}}
- {{#oauthMethods}}
- OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(oauthToken{{-index}});
-
- {{/oauthMethods}}
- {{/lambda.trimTrailingWithNewLine}}
- });
- })
- .Build();
-
- private readonly IHost _hostUsingAddWithAClient =
- Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) =>
- {
- services.Add{{apiName}}(options =>
- {
- {{#lambda.trimTrailingWithNewLine}}
- {{#apiKeyMethods}}
- ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(apiKeyToken{{-index}});
-
- {{/apiKeyMethods}}
- {{#httpBearerMethods}}
- BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(bearerToken{{-index}});
-
- {{/httpBearerMethods}}
- {{#httpBasicMethods}}
- BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(basicToken{{-index}});
-
- {{/httpBasicMethods}}
- {{#httpSignatureMethods}}
- HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0);
- HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(httpSignatureToken{{-index}});
-
- {{/httpSignatureMethods}}
- {{#oauthMethods}}
- OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1));
- options.AddTokens(oauthToken{{-index}});
-
- {{/oauthMethods}}
- {{/lambda.trimTrailingWithNewLine}}
- options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS));
- });
- })
- .Build();
-
- ///
- /// Test dependency injection when using the configure method
- ///
- [Fact]
- public void ConfigureApiWithAClientTest()
- {
- {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
- Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
-
- {{/-last}}{{/apis}}{{/apiInfo}}
- }
-
- ///
- /// Test dependency injection when using the configure method
- ///
- [Fact]
- public void ConfigureApiWithoutAClientTest()
- {
- {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
- Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
-
- {{/-last}}{{/apis}}{{/apiInfo}}
- }
-
- ///
- /// Test dependency injection when using the add method
- ///
- [Fact]
- public void AddApiWithAClientTest()
- {
- {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
- Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
-
- {{/-last}}{{/apis}}{{/apiInfo}}
- }
-
- ///
- /// Test dependency injection when using the add method
- ///
- [Fact]
- public void AddApiWithoutAClientTest()
- {
- {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
- Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}
-
- {{/-last}}{{/apis}}{{/apiInfo}}
- }
- }
-}
diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
deleted file mode 100644
index 8b69a8c0b..000000000
--- a/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache
+++ /dev/null
@@ -1,679 +0,0 @@
-//
-{{>partial_header}}
-
-{{#nrt}}
-#nullable enable
-
-{{/nrt}}
-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}}.{{clientPackage}}
-{
- ///
- /// Class for HttpSigning auth related parameter and methods
- ///
- {{>visibility}} class HttpSigningConfiguration
- {
- ///
- /// Create an instance
- ///
- public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{nrt?}} keyPassPhrase, List httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod)
- {
- KeyId = keyId;
- KeyFilePath = keyFilePath;
- KeyPassPhrase = keyPassPhrase;
- HttpSigningHeader = httpSigningHeader;
- HashAlgorithm = hashAlgorithm;
- SigningAlgorithm = signingAlgorithm;
- SignatureValidityPeriod = signatureValidityPeriod;
- }
-
- ///
- ///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{{nrt?}} 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; } = HashAlgorithmName.SHA256;
-
- ///
- /// Gets the signing algorithm
- ///
- public string SigningAlgorithm { get; set; }
-
- ///
- /// Gets the Signature validity period in seconds
- ///
- public int SignatureValidityPeriod { get; set; }
-
- private enum PrivateKeyType
- {
- None = 0,
- RSA = 1,
- ECDSA = 2,
- }
-
- ///
- /// Gets the Headers for HttpSigning
- ///
- ///
- ///
- ///
- internal Dictionary GetHttpSignedHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, System.Threading.CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
- {
- if (request.RequestUri == null)
- throw new NullReferenceException("The request URI was null");
-
- 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)");
-
- var dateTime = DateTime.Now;
- string digest = String.Empty;
-
- if (HashAlgorithm == HashAlgorithmName.SHA256)
- {
- var bodyDigest = GetStringHash(HashAlgorithm, requestBody);
- digest = string.Format("SHA-256={0}", Convert.ToBase64String(bodyDigest));
- }
- else if (HashAlgorithm == HashAlgorithmName.SHA512)
- {
- var bodyDigest = GetStringHash(HashAlgorithm, 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))
- httpSignatureHeader.Add(header.ToLower(), request.RequestUri.ToString());
- 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(), request.RequestUri.ToString());
- HttpSignedRequestHeader.Add(HEADER_HOST, request.RequestUri.ToString());
- }
- 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 request.Headers)
- {
- 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));
-
- //Concatenate headers value separated by new line
- var headerValuesString = string.Join("\n", headerValuesList);
- var signatureStringHash = GetStringHash(HashAlgorithm, headerValuesString);
- string{{nrt?}} 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(HashAlgorithmName hashAlgorithmName, string stringToBeHashed)
- {
- HashAlgorithm{{nrt?}} hashAlgorithm = null;
-
- if (hashAlgorithmName == HashAlgorithmName.SHA1)
- hashAlgorithm = SHA1.Create();
-
- if (hashAlgorithmName == HashAlgorithmName.SHA256)
- hashAlgorithm = SHA256.Create();
-
- if (hashAlgorithmName == HashAlgorithmName.SHA512)
- hashAlgorithm = SHA512.Create();
-
- if (hashAlgorithmName == HashAlgorithmName.MD5)
- hashAlgorithm = MD5.Create();
-
- if (hashAlgorithm == null)
- throw new NullReferenceException($"{ nameof(hashAlgorithm) } was null.");
-
- byte[] bytes = Encoding.UTF8.GetBytes(stringToBeHashed);
- byte[] 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)
- {
- if (KeyPassPhrase == null)
- throw new NullReferenceException($"{ nameof(KeyPassPhrase) } was null.");
-
- RSA{{nrt?}} rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase);
-
- if (rsa == null)
- return string.Empty;
- else 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)
- {
- {{#net60OrLater}}
- 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();
-
- var byteCount = 0;
- if (KeyPassPhrase != null)
- {
- IntPtr unmanagedString = IntPtr.Zero;
- try
- {
- // convert secure string to byte array
- unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(KeyPassPhrase);
-
- string ptrToStringUni = Marshal.PtrToStringUni(unmanagedString) ?? throw new NullReferenceException();
-
- ecdsa.ImportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(ptrToStringUni), 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;
- {{/net60OrLater}}
- {{^net60OrLater}}
- throw new Exception("ECDSA signing is supported only on NETCOREAPP3_0 and above");
- {{/net60OrLater}}
- }
-
- private byte[] ConvertToECDSAANS1Format(byte[] signedBytes)
- {
- var derBytes = new List();
- byte derLength = 68; //default length for ECDSA code signing 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 length, 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{{nrt?}} GetRSAProviderFromPemFile(String pemfile, SecureString{{nrt?}} keyPassPhrase = null)
- {
- const String pempubheader = "-----BEGIN PUBLIC KEY-----";
- const String pempubfooter = "-----END PUBLIC KEY-----";
- bool isPrivateKeyFile = true;
- byte[]{{nrt?}} 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, keyPassPhrase);
-
- if (pemkey == null)
- return null;
-
- return DecodeRSAPrivateKey(pemkey);
- }
- return null;
- }
-
- private byte[]{{nrt?}} ConvertPrivateKeyToBytes(String instr, SecureString{{nrt?}} keyPassPhrase = 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 (global::System.FormatException)
- {
- StringReader str = new StringReader(pvkstr);
-
- //-------- read PEM encryption info. lines and extract salt -----
- if (!str.ReadLine(){{nrt!}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null?
- return null;
-
- String saltline = str.ReadLine(){{nrt!}}; // TODO: what do we do here if ReadLine is null?
- 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 (global::System.FormatException)
- { //data is not in base64 format
- return null;
- }
-
- // TODO: what do we do here if keyPassPhrase is null?
- byte[] deskey = GetEncryptedKey(salt, keyPassPhrase{{nrt!}}, 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[]{{nrt?}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
-
- return rsakey;
- }
- }
-
- private RSACryptoServiceProvider{{nrt?}} 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 concatenated Mi hashed results
-
- byte[] psbytes = new byte[secpswd.Length];
- unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd);
- Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length);
- Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd);
-
- // --- concatenate 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 concatenate results D1, D2 ... into keymaterial bytes ----
- MD5 md5 = MD5.Create();
- byte[]{{nrt?}} 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{{nrt!}}, hashtarget, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
- 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); //concatenate 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{{nrt!}}, 0, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
- Array.Clear(hashtarget, 0, hashtarget.Length);
- Array.Clear(keymaterial, 0, keymaterial.Length);
- return deskey;
- }
-
- private byte[]{{nrt?}} 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;
- }
- }
-}
diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
deleted file mode 100644
index 881682e89..000000000
--- a/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-{{partial_header}}
-{{#nrt}}
-#nullable enable
-
-{{/nrt}}
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace {{packageName}}.{{clientPackage}}
-{
- ///
- /// A token constructed from an HttpSigningConfiguration
- ///
- {{>visibility}} class HttpSignatureToken : TokenBase
- {
- private HttpSigningConfiguration _configuration;
-
- ///
- /// Constructs an HttpSignatureToken object.
- ///
- ///
- ///
- public HttpSignatureToken(HttpSigningConfiguration configuration, TimeSpan? timeout = null) : base(timeout)
- {
- _configuration = configuration;
- }
-
- ///
- /// Places the token in the header.
- ///
- ///
- ///
- ///
- public void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
- {
- var signedHeaders = _configuration.GetHttpSignedHeader(request, requestBody, cancellationToken);
-
- foreach (var signedHeader in signedHeaders)
- request.Headers.Add(signedHeader.Key, signedHeader.Value);
- }
- }
-}
\ No newline at end of file
diff --git a/templates-v7/csharp/libraries/generichost/README.client.mustache b/templates-v7/csharp/libraries/generichost/README.client.mustache
index 247cbe399..3494e0cd0 100644
--- a/templates-v7/csharp/libraries/generichost/README.client.mustache
+++ b/templates-v7/csharp/libraries/generichost/README.client.mustache
@@ -79,7 +79,7 @@ namespace YourProject
{{#authMethods}}
{{#-first}}
// The type of token here depends on the api security specifications
- // Available token types are ApiKeyToken, BearerToken, HttpSigningToken, and OAuthToken.
+ // Available token types are ApiKeyToken, BearerToken, and OAuthToken.
BearerToken token = new("");
options.AddTokens(token);
diff --git a/templates-v7/csharp/libraries/generichost/api.mustache b/templates-v7/csharp/libraries/generichost/api.mustache
index 67264bf5a..e4c4372ec 100644
--- a/templates-v7/csharp/libraries/generichost/api.mustache
+++ b/templates-v7/csharp/libraries/generichost/api.mustache
@@ -173,12 +173,7 @@ namespace {{packageName}}.{{apiPackage}}
///
/// A token provider of type .
///
- public ITokenProvider BearerTokenProvider { get; }{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}}
-
- ///
- /// A token provider of type .
- ///
- public ITokenProvider HttpSignatureTokenProvider { get; }{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}
+ public ITokenProvider BearerTokenProvider { get; }{{/hasHttpBearerMethods}}{{#hasOAuthMethods}}
///
/// A token provider of type .
@@ -203,8 +198,7 @@ namespace {{packageName}}.{{apiPackage}}
///
public {{classname}}(ILogger<{{classname}}> logger, ILoggerFactory loggerFactory, System.Net.Http.HttpClient httpClient, JsonSerializerOptionsProvider jsonSerializerOptionsProvider, {{classname}}Events {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}}{{#hasApiKeyMethods}},
ITokenProvider apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}},
- ITokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}},
- ITokenProvider httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}},
+ ITokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasOAuthMethods}},
ITokenProvider oauthTokenProvider{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}},
{{packageName}}.{{clientPackage}}.CookieContainer cookieContainer{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}})
{
@@ -214,8 +208,7 @@ namespace {{packageName}}.{{apiPackage}}
HttpClient = httpClient;
Events = {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}};{{#hasApiKeyMethods}}
ApiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}
- BearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}}
- HttpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}
+ BearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasOAuthMethods}}
OauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}}
CookieContainer = cookieContainer;{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}}
}
@@ -373,16 +366,6 @@ namespace {{packageName}}.{{apiPackage}}
OAuthToken oauthToken{{-index}} = (OAuthToken) await OauthTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false);
oauthToken{{-index}}.UseInHeader(httpRequestMessage, "{{keyParamName}}");
{{/isOAuth}}
- {{#isHttpSignature}}
-
- HttpSignatureToken httpSignatureToken{{-index}} = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false);
-
- if (httpRequestMessage.Content != null) {
- string requestBody = await httpRequestMessage.Content.ReadAsStringAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false);
-
- httpSignatureToken{{-index}}.UseInHeader(httpRequestMessage, requestBody, cancellationToken);
- }
- {{/isHttpSignature}}
{{/authMethods}}
{{#consumes}}
{{#-first}}