From 7dfcdeed425428254ff80ad776955b7bb933525d Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Mon, 18 Oct 2021 18:06:12 -0400 Subject: [PATCH 01/35] added generichost library --- .../languages/CSharpNetCoreClientCodegen.java | 80 ++++++++++++++++--- .../csharp-netcore/modelAnyOf.mustache | 2 + .../csharp-netcore/modelOneOf.mustache | 2 + .../csharp-netcore/netcore_project.mustache | 5 ++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index ab5cd5bb3d0e..081fec491c0f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -50,6 +50,7 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { // HTTP libraries protected static final String RESTSHARP = "restsharp"; protected static final String HTTPCLIENT = "httpclient"; + protected static final String GENERICHOST = "generichost"; // Project Variable, determined from target framework. Not intended to be user-settable. protected static final String TARGET_FRAMEWORK_IDENTIFIER = "targetFrameworkIdentifier"; @@ -309,8 +310,10 @@ public CSharpNetCoreClientCodegen() { regexModifiers.put('s', "Singleline"); regexModifiers.put('x', "IgnorePatternWhitespace"); + supportedLibraries.put(GENERICHOST, "HttpClient with Generic Host dependency injection (https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host) " + + "(Experimental. Subject to breaking changes without notice.)"); supportedLibraries.put(HTTPCLIENT, "HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) " - + "(Experimental. May subject to breaking changes without further notice.)"); + + "(Experimental. Subject to breaking changes without notice.)"); supportedLibraries.put(RESTSHARP, "RestSharp (https://github.com/restsharp/RestSharp)"); CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "HTTP library template (sub-template) to use"); @@ -567,7 +570,10 @@ public void processOpts() { clientPackage = "Client"; - if (RESTSHARP.equals(getLibrary())) { + if (GENERICHOST.equals(getLibrary())){ + setLibrary(GENERICHOST); + additionalProperties.put("useGenericHost", true); + } else if (RESTSHARP.equals(getLibrary())) { additionalProperties.put("useRestSharp", true); needsCustomHttpMethod = true; } else if (HTTPCLIENT.equals(getLibrary())) { @@ -669,11 +675,30 @@ public void processOpts() { binRelativePath += "vendor"; additionalProperties.put("binRelativePath", binRelativePath); + // Only write out test related files if excludeTests is unset or explicitly set to false (see start of this method) + if (Boolean.FALSE.equals(excludeTests.get())) { + modelTestTemplateFiles.put("model_test.mustache", ".cs"); + apiTestTemplateFiles.put("api_test.mustache", ".cs"); + } + if(HTTPCLIENT.equals(getLibrary())) { supportingFiles.add(new SupportingFile("FileParameter.mustache", clientPackageDir, "FileParameter.cs")); typeMapping.put("file", "FileParameter"); + addRestSharpSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); } + else if (GENERICHOST.equals(getLibrary())){ + addGenericHostSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); + } + else{ + addRestSharpSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); + } + + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); + } + public void addRestSharpSupportingFiles(final String clientPackageDir, final String packageFolder, + final AtomicReference excludeTests, final String testPackageFolder, final String testPackageName, final String modelPackageDir){ supportingFiles.add(new SupportingFile("IApiAccessor.mustache", clientPackageDir, "IApiAccessor.cs")); supportingFiles.add(new SupportingFile("Configuration.mustache", clientPackageDir, "Configuration.cs")); supportingFiles.add(new SupportingFile("ApiClient.mustache", clientPackageDir, "ApiClient.cs")); @@ -707,12 +732,6 @@ public void processOpts() { supportingFiles.add(new SupportingFile("GlobalConfiguration.mustache", clientPackageDir, "GlobalConfiguration.cs")); - // Only write out test related files if excludeTests is unset or explicitly set to false (see start of this method) - if (Boolean.FALSE.equals(excludeTests.get())) { - modelTestTemplateFiles.put("model_test.mustache", ".cs"); - apiTestTemplateFiles.put("api_test.mustache", ".cs"); - } - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); @@ -726,9 +745,50 @@ public void processOpts() { supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); + } - additionalProperties.put("apiDocPath", apiDocPath); - additionalProperties.put("modelDocPath", modelDocPath); + public void addGenericHostSupportingFiles(final String clientPackageDir, final String packageFolder, + final AtomicReference excludeTests, final String testPackageFolder, final String testPackageName, final String modelPackageDir){ + supportingFiles.add(new SupportingFile("TokenProvider`1.mustache", clientPackageDir, "TokenProvider`1.cs")); + supportingFiles.add(new SupportingFile("TokenContainer`1.mustache", clientPackageDir, "TokenContainer`1.cs")); + supportingFiles.add(new SupportingFile("TokenBase.mustache", clientPackageDir, "TokenBase.cs")); + supportingFiles.add(new SupportingFile("ApiException.mustache", clientPackageDir, "ApiException.cs")); + supportingFiles.add(new SupportingFile("ApiResponse`1.mustache", clientPackageDir, "ApiResponse`1.cs")); + supportingFiles.add(new SupportingFile("ClientUtils.mustache", clientPackageDir, "ClientUtils.cs")); + supportingFiles.add(new SupportingFile("HostConfiguration.mustache", clientPackageDir, "HostConfiguration.cs")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); + supportingFiles.add(new SupportingFile("netcore_project.mustache", packageFolder, packageName + ".csproj")); + supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); + supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); + supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", clientPackageDir, "OpenAPIDateConverter.cs")); + + if (Boolean.FALSE.equals(excludeTests.get())) { + supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); + } + + if (ProcessUtils.hasHttpSignatureMethods(openAPI)) { + supportingFiles.add(new SupportingFile("HttpSigningConfiguration.mustache", clientPackageDir, "HttpSigningConfiguration.cs")); + supportingFiles.add(new SupportingFile("HttpSigningToken.mustache", clientPackageDir, "HttpSigningToken.cs")); + } + + if (ProcessUtils.hasHttpBasicMethods(openAPI)) { + supportingFiles.add(new SupportingFile("BasicToken.mustache", clientPackageDir, "BasicToken.cs")); + } + + if (ProcessUtils.hasOAuthMethods(openAPI)) { + supportingFiles.add(new SupportingFile("OAuthToken.mustache", clientPackageDir, "OAuthToken.cs")); + } + + if (ProcessUtils.hasHttpBearerMethods(openAPI)) { + supportingFiles.add(new SupportingFile("BearerToken.mustache", clientPackageDir, "BearerToken.cs")); + } + + if (ProcessUtils.hasApiKeyMethods(openAPI)) { + supportingFiles.add(new SupportingFile("ApiKeyToken.mustache", clientPackageDir, "ApiKeyToken.cs")); + } } public void setNetStandard(Boolean netStandard) { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/modelAnyOf.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/modelAnyOf.mustache index 7c3b0cc2d94a..f216a9911309 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/modelAnyOf.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/modelAnyOf.mustache @@ -171,6 +171,7 @@ } } + {{#validatable}} /// /// To validate all properties of the instance /// @@ -180,6 +181,7 @@ { yield break; } + {{/validatable}} } /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/modelOneOf.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/modelOneOf.mustache index d99b82a1e9e6..f60d3cf10f38 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/modelOneOf.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/modelOneOf.mustache @@ -216,6 +216,7 @@ } } + {{#validatable}} /// /// To validate all properties of the instance /// @@ -225,6 +226,7 @@ { yield break; } + {{/validatable}} } /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache index 3be35749bea8..8b28eb956785 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache @@ -33,6 +33,11 @@ {{#useRestSharp}} {{/useRestSharp}} + {{#useGenericHost}} + + + + {{/useGenericHost}} {{#supportsRetry}} {{/supportsRetry}} From 2a1c275333cd3ecf57e2e167f3761a9b24a3f58a Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Mon, 18 Oct 2021 18:07:50 -0400 Subject: [PATCH 02/35] added templates --- .../generichost/ApiException.mustache | 44 ++ .../generichost/ApiKeyToken.mustache | 63 ++ .../generichost/ApiResponse`1.mustache | 96 +++ .../libraries/generichost/BasicToken.mustache | 44 ++ .../generichost/BearerToken.mustache | 39 + .../generichost/ClientUtils.mustache | 328 +++++++++ .../generichost/HostConfiguration.mustache | 77 ++ .../HttpSigningConfiguration.mustache | 676 ++++++++++++++++++ .../generichost/HttpSigningToken.mustache | 43 ++ .../libraries/generichost/OAuthToken.mustache | 39 + .../libraries/generichost/README.mustache | 189 +++++ .../libraries/generichost/TokenBase.mustache | 62 ++ .../generichost/TokenContainer`1.mustache | 36 + .../generichost/TokenProvider`1.mustache | 49 ++ .../libraries/generichost/api.mustache | 289 ++++++++ 15 files changed, 2074 insertions(+) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiException.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiException.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiException.mustache new file mode 100644 index 000000000000..77e95ca3d144 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiException.mustache @@ -0,0 +1,44 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; + +namespace {{packageName}}.Client +{ + /// + /// API Exception + /// + {{>visibility}} class ApiException : Exception + { + /// + /// The reason the api request failed + /// + public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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 reponse + /// + /// + /// + /// + public ApiException(string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent) + { + ReasonPhrase = reasonPhrase; + + StatusCode = statusCode; + + RawContent = rawContent; + } + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache new file mode 100644 index 000000000000..86292f032f92 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache @@ -0,0 +1,63 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; + +namespace {{packageName}}.Client +{ + /// + /// A token constructed from an apiKey. + /// + public class ApiKeyToken : TokenBase + { + private string _raw; + + private bool _useCookieContainer; + + /// + /// Constructs an ApiKeyToken object. + /// + /// + /// + /// + public ApiKeyToken(TimeSpan tokenTimeOut, string value, bool useCookieContainer = false) : base(tokenTimeOut) + { + _raw = value; + + _useCookieContainer = useCookieContainer; + } + /// + /// Places the token in the cookie. + /// + /// + /// + public virtual void UseInCookie(System.Net.Http.HttpRequestMessage request, string cookieName) + { + if (!_useCookieContainer) + request.Headers.Add("Cookie", $"{ cookieName }=_raw"); + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(System.Net.Http.HttpRequestMessage request, string headerName) + { + request.Headers.Add(headerName, _raw); + } + + /// + /// Places the token in the query. + /// + /// + /// + /// + /// + public virtual void UseInQuery(System.Net.Http.HttpRequestMessage request, UriBuilder uriBuilder, System.Collections.Specialized.NameValueCollection parseQueryString, string parameterName) + { + parseQueryString[parameterName] = Uri.EscapeDataString(_raw).ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}; + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache new file mode 100644 index 000000000000..3c618b0b07aa --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache @@ -0,0 +1,96 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Collections.Generic; +using System.Net; +using Newtonsoft.Json; + +namespace {{packageName}}.Client +{ + /// + /// Provides a non-generic contract for the ApiResponse wrapper. + /// + public interface IApiResponse + { + /// + /// The data type of + /// + Type ResponseType { get; } + + /// + /// Gets or sets the status code (HTTP status code) + /// + /// The status code. + HttpStatusCode StatusCode { get; } + + /// + /// The raw content of this response + /// + string RawContent { get; } + } + + /// + /// API Response + /// + {{>visibility}} partial class ApiResponse : IApiResponse + { + #region Properties + + /// + /// The deserialized content + /// + public T{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} Content { get; set; } + + /// + /// Gets or sets the status code (HTTP status code) + /// + /// The status code. + public HttpStatusCode StatusCode { get; } + + /// + /// The content of this response + /// + public Type ResponseType + { + get { return typeof(T); } + } + + /// + /// The raw data + /// + public string RawContent { get; } + + /// + /// The IsSuccessStatusCode from the api response + /// + public bool IsSuccessStatusCode { get; } + + /// + /// The reason phrase contained in the api response + /// + public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; } + + /// + /// The headers contained in the api response + /// + public System.Net.Http.Headers.HttpResponseHeaders Headers { get; } + + #endregion Properties + + /// + /// Construct the reponse using an HttpResponseMessage + /// + /// + /// + public ApiResponse(System.Net.Http.HttpResponseMessage response, string rawContent) + { + StatusCode = response.StatusCode; + Headers = response.Headers; + IsSuccessStatusCode = response.IsSuccessStatusCode; + ReasonPhrase = response.ReasonPhrase; + RawContent = rawContent; + } + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache new file mode 100644 index 000000000000..822ce728945c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache @@ -0,0 +1,44 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.Client +{ + /// + /// A token constructed from a username and password. + /// + public class BasicToken : TokenBase + { + private string _username; + + private string _password; + + /// + /// Constructs a BasicToken object. + /// + /// + /// + /// + public BasicToken(TimeSpan tokenTimeOut, string username, string password) : base(tokenTimeOut) + { + _username = username; + + _password = password; + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(System.Net.Http.HttpRequestMessage request, string headerName) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", {{packageName}}.Client.ClientUtils.Base64Encode(_username + ":" + _password)); + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache new file mode 100644 index 000000000000..887b36439c37 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache @@ -0,0 +1,39 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.Client +{ + /// + /// A token constructed from a token from a bearer token. + /// + public class BearerToken : TokenBase + { + private string _raw; + + /// + /// Constructs a BearerToken object. + /// + /// + /// + public BearerToken(TimeSpan tokenTimeOut, string value) : base(tokenTimeOut) + { + _raw = value; + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(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/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache new file mode 100644 index 000000000000..badf8272117c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -0,0 +1,328 @@ +{{>partial_header}} + +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Polly.Timeout; +using Polly.Extensions.Http; +using Polly; +using System.Net.Http; +using {{packageName}}.Api; +{{#useCompareNetObjects}} +using KellermanSoftware.CompareNetObjects; +{{/useCompareNetObjects}} + +namespace {{packageName}}.Client +{ + /// + /// Utility functions providing some benefit to API client consumers. + /// + public static class ClientUtils + { + {{#useCompareNetObjects}} + /// + /// An instance of CompareLogic. + /// + public static CompareLogic compareLogic; + + /// + /// Static constructor to initialise compareLogic. + /// + static ClientUtils() + { + compareLogic = new CompareLogic(); + } + {{/useCompareNetObjects}} + + /// + /// Custom JSON serializer + /// + public static Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get; set; } = new Newtonsoft.Json.JsonSerializerSettings + { + // OpenAPI generated types generally hide default constructors. + ConstructorHandling = Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor, + MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore, + ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver + { + NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy + { + OverrideSpecifiedNames = false + } + } + }; + + /// + /// Sanitize filename by removing the path + /// + /// Filename + /// Filename + public static string SanitizeFilename(string filename) + { + Match match = Regex.Match(filename, @".*[/\\](.*)$"); + return match.Success ? match.Groups[1].Value : filename; + } + + /// + /// 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{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ParameterToString(object obj, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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); + if (obj is bool boolean) + return boolean ? "true" : "false"; + if (obj is System.Collections.ICollection collection) + return string.Join(",", collection.Cast()); + + 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(System.Text.Encoding.UTF8.GetBytes(text)); + } + + /// + /// Convert stream to byte array + /// + /// Input stream to be converted + /// Byte array + public static byte[] ReadAsBytes(Stream inputStream) + { + using (var ms = new MemoryStream()) + { + inputStream.CopyTo(ms); + return ms.ToArray(); + } + } + + /// + /// Select the Content-Type header's value from the given content-type array: + /// if JSON type exists in the given array, use it; + /// otherwise use the first one defined in 'consumes' + /// + /// The Content-Type array to select from. + /// The Content-Type header to use. + public static string SelectHeaderContentType(string[] contentTypes) + { + if (contentTypes.Length == 0) + return null; + + foreach (var contentType in contentTypes) + { + if (IsJsonMime(contentType)) + return contentType; + } + + return contentTypes[0]; // use the first content type specified in 'consumes' + } + + /// + /// Select the Accept header's value from the given accepts array: + /// if JSON exists in the given array, use it; + /// otherwise use all of them (joining into a string) + /// + /// The accepts array to select from. + /// The Accept header to use. + public static string SelectHeaderAccept(string[] accepts) + { + if (accepts.Length == 0) + return null; + + if (accepts.Contains("application/json", StringComparer.OrdinalIgnoreCase)) + return "application/json"; + + return string.Join(",", accepts); + } + + /// + /// Provides a case-insensitive check that a provided content type is a known JSON-like content type. + /// + public static readonly Regex JsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"); + + /// + /// Check if the given MIME is a JSON MIME. + /// JSON MIME examples: + /// application/json + /// application/json; charset=UTF8 + /// APPLICATION/JSON + /// application/vnd.company+json + /// + /// MIME + /// Returns True if MIME type is json. + public static bool IsJsonMime(string mime) + { + if (string.IsNullOrWhiteSpace(mime)) return false; + + return JsonRegex.IsMatch(mime) || mime.Equals("application/json-patch+json"); + } + + /// + /// The base path of the API + /// + public const string BASE_ADDRESS = "{{{basePath}}}"; + + /// + /// The scheme of the API + /// + public const string SCHEME = "{{{scheme}}}"; + + /// + /// The context path of the API + /// + public const string CONTEXT_PATH = "{{contextPath}}"; + + /// + /// The host of the API + /// + public const string HOST = "{{{host}}}"; + + /// + /// The format to use for DateTime serialization + /// + public const string ISO8601_DATETIME_FORMAT = "o"; + + /// + /// Add the api to your host builder. + /// + /// + /// + public static IHostBuilder Configure{{packageName}}(this IHostBuilder builder, Action options) + { + builder.ConfigureServices((context, services) => + { + HostConfiguration config = new(services); + + options(context, config); + }); + + return builder; + } + + /// + /// Add the api to your host builder. + /// + /// + /// + public static void Add{{packageName}}(this IServiceCollection services, Action options) + { + HostConfiguration config = new(services); + options(config); + } + + /// + /// 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 circiut 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); + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache new file mode 100644 index 000000000000..5106e9cf76e7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -0,0 +1,77 @@ +{{>partial_header}} + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using CocApi.Api; + +namespace {{packageName}}.Client +{ + /// + /// Provides hosting configuration for {{packageName}} + /// + public class HostConfiguration + { + private readonly IServiceCollection _services; + private readonly List _clients = new(); + + /// + /// Instantiates the class + /// + /// + public HostConfiguration(IServiceCollection services) + { + _services = services; + {{#apiInfo}}{{#apis}}_clients.Add(services.AddHttpClient<{{interfacePrefix}}{{classname}}, {{classname}}>(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)));{{^-last}} + {{/-last}}{{/apis}}{{/apiInfo}} + + {{#hasAuthMethods}}{{#hasApiKeyMethods}}services.AddSingleton>();{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + services.AddSingleton>();{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + services.AddSingleton>();{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + services.AddSingleton>();{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + services.AddSingleton>();{{/hasOAuthMethods}} + + {{/hasAuthMethods}} + {{#apiInfo}}{{#apis}}services.AddTransient<{{interfacePrefix}}{{classname}}, {{classname}}>();{{^-last}} + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Configures the HttpClients. Use this to add your middleware. + /// + /// + /// + public HostConfiguration ConfigureClients(Action clientBuilder) + { + foreach(IHttpClientBuilder client in _clients) + clientBuilder(client); + + return this; + } + + /// + /// Configures the JsonSerializerSettings + /// + /// + /// + public HostConfiguration ConfigureJsonOptions(Action options) + { + options(Client.ClientUtils.JsonSerializerSettings); + + return this; + } + + /// + /// Adds tokens to your IServiceCollection + /// + /// + /// + /// + public HostConfiguration AddTokens(IEnumerable tokens) where T : TokenBase + { + _services.AddSingleton(new TokenContainer(tokens)); + + return this; + } + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache new file mode 100644 index 000000000000..2f93e5fba74f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache @@ -0,0 +1,676 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Web; + +namespace {{packageName}}.Client +{ + /// + /// Class for HttpSigning auth related parameter and methods + /// + public class HttpSigningConfiguration + { + #region + /// + /// Create an instance + /// + public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPhrase, List httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod) + { + KeyId = keyId; + KeyFilePath = keyFilePath; + KeyPassPhrase = keyPassPhrase; + HttpSigningHeader = httpSigningHeader; + HashAlgorithm = hashAlgorithm; + SigningAlgorithm = signingAlgorithm; + SignatureValidityPeriod = signatureValidityPeriod; + } + #endregion + + #region Properties + /// + ///Gets the Api keyId + /// + public string KeyId { get; set; } + + /// + /// Gets the Key file path + /// + public string KeyFilePath { get; set; } + + /// + /// Gets the key pass phrase for password protected key + /// + public SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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 validaty period in seconds + /// + public int SignatureValidityPeriod { get; set; } + + #endregion + + #region enum + private enum PrivateKeyType + { + None = 0, + RSA = 1, + ECDSA = 2, + } + #endregion + + #region Methods + /// + /// Gets the Headers for HttpSigning + /// + /// + /// + /// + internal Dictionary GetHttpSignedHeader(System.Net.Http.HttpRequestMessage request, string requestBody, System.Threading.CancellationToken? cancellationToken = null) + { + 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.ToString(), requestBody); + digest = string.Format("SHA-256={0}", Convert.ToBase64String(bodyDigest)); + } + else if (HashAlgorithm == HashAlgorithmName.SHA512) + { + var bodyDigest = GetStringHash(HashAlgorithm.ToString(), requestBody); + digest = string.Format("SHA-512={0}", Convert.ToBase64String(bodyDigest)); + } + else + throw new Exception(string.Format("{0} not supported", HashAlgorithm)); + + foreach (var header in HttpSigningHeader) + if (header.Equals(HEADER_REQUEST_TARGET)) + 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)); + + //Concatinate headers value separated by new line + var headerValuesString = string.Join("\n", headerValuesList); + var signatureStringHash = GetStringHash(HashAlgorithm.ToString(), headerValuesString); + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} headerSignatureStr = null; + var keyType = GetKeyType(KeyFilePath); + + if (keyType == PrivateKeyType.RSA) + headerSignatureStr = GetRSASignature(signatureStringHash); + + else if (keyType == PrivateKeyType.ECDSA) + headerSignatureStr = GetECDSASignature(signatureStringHash); + + var cryptographicScheme = "hs2019"; + var authorizationHeaderValue = string.Format("Signature keyId=\"{0}\",algorithm=\"{1}\"", + KeyId, cryptographicScheme); + + if (httpSignatureHeader.ContainsKey(HEADER_CREATED)) + authorizationHeaderValue += string.Format(",created={0}", httpSignatureHeader[HEADER_CREATED]); + + if (httpSignatureHeader.ContainsKey(HEADER_EXPIRES)) + authorizationHeaderValue += string.Format(",expires={0}", httpSignatureHeader[HEADER_EXPIRES]); + + authorizationHeaderValue += string.Format(",headers=\"{0}\",signature=\"{1}\"", headersKeysString, headerSignatureStr); + + HttpSignedRequestHeader.Add(HEADER_AUTHORIZATION, authorizationHeaderValue); + + return HttpSignedRequestHeader; + } + + private byte[] GetStringHash(string hashName, string stringToBeHashed) + { + var hashAlgorithm = System.Security.Cryptography.HashAlgorithm.Create(hashName); + + if (hashAlgorithm == null) + throw new NullReferenceException($"{ nameof(hashAlgorithm) } was null."); + + var bytes = Encoding.UTF8.GetBytes(stringToBeHashed); + var stringHash = hashAlgorithm.ComputeHash(bytes); + return stringHash; + } + + private int GetUnixTime(DateTime date2) + { + DateTime date1 = new DateTime(1970, 01, 01); + TimeSpan timeSpan = date2 - date1; + return (int)timeSpan.TotalSeconds; + } + + private string GetRSASignature(byte[] stringToSign) + { + if (KeyPassPhrase == null) + throw new NullReferenceException($"{ nameof(KeyPassPhrase) } was null."); + + RSA{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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) + { + if (!File.Exists(KeyFilePath)) + { + throw new Exception("key file path does not exist."); + } + + var ecKeyHeader = "-----BEGIN EC PRIVATE KEY-----"; + var ecKeyFooter = "-----END EC PRIVATE KEY-----"; + var keyStr = File.ReadAllText(KeyFilePath); + var ecKeyBase64String = keyStr.Replace(ecKeyHeader, "").Replace(ecKeyFooter, "").Trim(); + var keyBytes = System.Convert.FromBase64String(ecKeyBase64String); + var ecdsa = ECDsa.Create(); + +#if (NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0) + var byteCount = 0; + if (KeyPassPhrase != null) + { + IntPtr unmanagedString = IntPtr.Zero; + try + { + // convert secure string to byte array + unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(KeyPassPhrase); + + 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; +#else + throw new Exception("ECDSA signing is supported only on NETCOREAPP3_0 and above"); +#endif + + } + + private byte[] ConvertToECDSAANS1Format(byte[] signedBytes) + { + var derBytes = new List(); + byte derLength = 68; //default lenght for ECDSA code signinged bit 0x44 + byte rbytesLength = 32; //R length 0x20 + byte sbytesLength = 32; //S length 0x20 + var rBytes = new List(); + var sBytes = new List(); + for (int i = 0; i < 32; i++) + rBytes.Add(signedBytes[i]); + + for (int i = 32; i < 64; i++) + sBytes.Add(signedBytes[i]); + + if (rBytes[0] > 0x7F) + { + derLength++; + rbytesLength++; + var tempBytes = new List(); + tempBytes.AddRange(rBytes); + rBytes.Clear(); + rBytes.Add(0x00); + rBytes.AddRange(tempBytes); + } + + if (sBytes[0] > 0x7F) + { + derLength++; + sbytesLength++; + var tempBytes = new List(); + tempBytes.AddRange(sBytes); + sBytes.Clear(); + sBytes.Add(0x00); + sBytes.AddRange(tempBytes); + + } + + derBytes.Add(48); //start of the sequence 0x30 + derBytes.Add(derLength); //total length r lenth, type and r bytes + + derBytes.Add(2); //tag for integer + derBytes.Add(rbytesLength); //length of r + derBytes.AddRange(rBytes); + + derBytes.Add(2); //tag for integer + derBytes.Add(sbytesLength); //length of s + derBytes.AddRange(sBytes); + return derBytes.ToArray(); + } + + private RSACryptoServiceProvider{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} GetRSAProviderFromPemFile(String pemfile, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPharse = null) + { + const String pempubheader = "-----BEGIN PUBLIC KEY-----"; + const String pempubfooter = "-----END PUBLIC KEY-----"; + bool isPrivateKeyFile = true; + byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} pemkey = null; + + if (!File.Exists(pemfile)) + throw new Exception("private key file does not exist."); + + string pemstr = File.ReadAllText(pemfile).Trim(); + + if (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter)) + isPrivateKeyFile = false; + + if (isPrivateKeyFile) + { + pemkey = ConvertPrivateKeyToBytes(pemstr, keyPassPharse); + + if (pemkey == null) + return null; + + return DecodeRSAPrivateKey(pemkey); + } + return null; + } + + private byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ConvertPrivateKeyToBytes(String instr, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPharse = null) + { + const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----"; + const String pemprivfooter = "-----END RSA PRIVATE KEY-----"; + String pemstr = instr.Trim(); + byte[] binkey; + + if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) + return null; + + StringBuilder sb = new StringBuilder(pemstr); + sb.Replace(pemprivheader, ""); + sb.Replace(pemprivfooter, ""); + String pvkstr = sb.ToString().Trim(); + + try + { // if there are no PEM encryption info lines, this is an UNencrypted PEM private key + binkey = Convert.FromBase64String(pvkstr); + return binkey; + } + catch (System.FormatException) + { + StringReader str = new StringReader(pvkstr); + + //-------- read PEM encryption info. lines and extract salt ----- + if (!str.ReadLine(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null? + return null; + + String saltline = str.ReadLine(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}; // 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 (System.FormatException) + { //data is not in base64 fromat + return null; + } + + // TODO: what do we do here if keyPassPharse is null? + byte[] deskey = GetEncryptedKey(salt, keyPassPharse{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, 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[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV + + return rsakey; + } + } + + private RSACryptoServiceProvider{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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 = new MD5CryptoServiceProvider(); + byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} 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{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, hashtarget, result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.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{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, 0, result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.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[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) + { + MemoryStream memst = new MemoryStream(); + TripleDES alg = TripleDES.Create(); + alg.Key = desKey; + alg.IV = IV; + try + { + CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(cipherData, 0, cipherData.Length); + cs.Close(); + } + catch (Exception) + { + return null; + } + byte[] decryptedData = memst.ToArray(); + return decryptedData; + } + + /// + /// Detect the key type from the pem file. + /// + /// key file path in pem format + /// + private PrivateKeyType GetKeyType(string keyFilePath) + { + if (!File.Exists(keyFilePath)) + throw new Exception("Key file path does not exist."); + + var ecPrivateKeyHeader = "BEGIN EC PRIVATE KEY"; + var ecPrivateKeyFooter = "END EC PRIVATE KEY"; + var rsaPrivateKeyHeader = "BEGIN RSA PRIVATE KEY"; + var rsaPrivateFooter = "END RSA PRIVATE KEY"; + //var pkcs8Header = "BEGIN PRIVATE KEY"; + //var pkcs8Footer = "END PRIVATE KEY"; + var keyType = PrivateKeyType.None; + var key = File.ReadAllLines(keyFilePath); + + if (key[0].ToString().Contains(rsaPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(rsaPrivateFooter)) + keyType = PrivateKeyType.RSA; + else if (key[0].ToString().Contains(ecPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter)) + keyType = PrivateKeyType.ECDSA; + + else if (key[0].ToString().Contains(ecPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter)) + { + /* this type of key can hold many type different types of private key, but here due lack of pem header + Considering this as EC key + */ + //TODO :- update the key based on oid + keyType = PrivateKeyType.ECDSA; + } + else + throw new Exception("Either the key is invalid or key is not supported"); + + return keyType; + } + #endregion + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache new file mode 100644 index 000000000000..af97f5b17156 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache @@ -0,0 +1,43 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.Client +{ + /// + /// A token constructed from an HttpSigningConfiguration + /// + public class HttpSignatureToken : TokenBase + { + private HttpSigningConfiguration _configuration; + + /// + /// Constructs an HttpSignatureToken object. + /// + /// + /// + public HttpSignatureToken(TimeSpan tokenTimeOut, HttpSigningConfiguration configuration) : base(tokenTimeOut) + { + _configuration = configuration; + } + + /// + /// Places the token in the header. + /// + /// + /// + /// + public void UseInHeader(System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken) + { + 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/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache new file mode 100644 index 000000000000..eca08a92f07f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache @@ -0,0 +1,39 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.Client +{ + /// + /// A token constructed with OAuth. + /// + public class OAuthToken : TokenBase + { + private string _raw; + + /// + /// Consturcts an OAuthToken object. + /// + /// + /// + public OAuthToken(TimeSpan tokenTimeOut, string value) : base(tokenTimeOut) + { + _raw = value; + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(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/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache new file mode 100644 index 000000000000..cf2944715368 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -0,0 +1,189 @@ +# Created with Openapi Generator + + +## Run the following powershell command to generate the library + +``` +java -jar "/openapi-generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar" generate ` + -g csharp-netcore ` + --library generichost ` + -i .yml ` + -o ` + --additional-properties="packageName=YourPackageName,targetFramework=net5.0,validatable=false,nullableReferenceTypes=true" +``` + + +## Using the library in your project + +``` +cs +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using {{packageName}}.Api; +using {{packageName}}.Client; +using {{packageName}}.Model; + +namespace YourProject +{ + public class Program + { + public static async Task Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + + var api = host.Services.GetRequiredService<{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#-first}}{{#operation}}{{operationId}}>(); + + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .Configure{{packageName}}((context, options) => + { + // the type of token here depends on the api security specifications + ApiKeyToken token = new(TimeSpan.FromSeconds(1), $"Bearer "); + ApiKeyToken[] tokens = new ApiKeyToken[] { token }; + options.AddTokens(tokens); + + options.ConfigureJsonOptions((jsonOptions) => + { + jsonOptions.Converters.Add(new DateTimeOffsetJsonConverter()); + jsonOptions.Converters.Add(new JsonStringEnumConverter()); + }); + + options.ConfigureClients(client => client + .AddRetryPolicy(2) + .AddTimeoutPolicy(TimeSpan.FromSeconds(5)) + .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)) + // add whatever middleware you prefer + ); + }); + } +} +``` + +## Questions + +- What about HttpRequest failures and retries? + You can add Polly or any other middleware in the ConfigureClients method. +- What about rate limits? + A token has a TimeSpan which controls how fast it will be reused. +- 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. + + +## Notes + +- If you are not using .Net Standard, consider setting validatable to false in your generator-config.json due to this bug: https://github.com/dotnet/project-system/issues/3934 +- If you want to override the provided mustache templates, provide generate the library with the -t parameter and the folder name which stores your templates.{{#netStandard}} + + +## Frameworks supported + +- .NET Core >=1.0 +- .NET Framework >=4.6 +- Mono/Xamarin >=vNext{{/netStandard}} + + +## Dependencies + +- [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) - 12.0.3 or later +- [JsonSubTypes](https://www.nuget.org/packages/JsonSubTypes/) - 1.7.0 or later{{#useCompareNetObjects}} +- [CompareNETObjects](https://www.nuget.org/packages/CompareNETObjects) - 4.61.0 or later{{/useCompareNetObjects}}{{#validatable}} +- [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 4.7.0 or later{{/validatable}} + + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | -------------{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + + +## Documentation for Models + +{{#modelPackage}}{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md){{/model}}{{/models}}{{/modelPackage}} +{{^modelPackage}}No model defined in this package{{/modelPackage}} + + +## Documentation for Authorization + +{{^authMethods}}All endpoints do not require authorization.{{/authMethods}}{{#authMethods}}{{#-last}}Authentication schemes defined for the API:{{/-last}}{{/authMethods}}{{#authMethods}} + + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}{{/isApiKey}}{{#isBasicBasic}} +- **Type**: HTTP basic authentication{{/isBasicBasic}}{{#isBasicBearer}} +- **Type**: Bearer Authentication{{/isBasicBearer}}{{#isOAuth}} +- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}}{{#scopes}} +- {{scope}}: {{description}}{{/scopes}}{{/isOAuth}}{{/authMethods}} + +## Api Configuration +- appName: {{appName}} +- appVersion: {{appVersion}} +- appDescription: {{appDescription}} + +## OpenApi Global properties +- debugOpenAPI: {{debugOpenAPI}} +- debugModels: {{debugModels}} +- debugOperations: {{debugOperations}} +- debugSupportingFiles: {{debugSupportingFiles}} +- verbose: {{verbose}} +- generateAliasAsModel: {{generateAliasAsModel}} +- org.openapitools.codegenutils.oncelogger.enabled: {{org.openapi.tools.codegenutils.oncelogger.enabled}} +- supportingFiles: {{supportingFiles}} +- models: omitted for brevity +- apis: omitted for brevity +- apiDocs: omitted for brevity +- modelDocs: {{modelDocs}} +- apiTests: omitted for brevity +- modelTests: {{modelTest}} +- withXml: {{withXml}} + +## OpenApi Generator Parameteres +- SDK version: {{packageVersion}}{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}}{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +- Package name: {{packageName}} +- allowUnicodeIdentifiers {{allowUnicodeIdentifiers}} +- caseInsensitiveResponseHeaders {{caseInsensitiveResponseHeaders}} +- disallowAdditionalPropertiesIfNotPresent {{disallowAdditionalPropertiesIfNotPresent}} +- hideGenerationTimestamp: {{hideGenerationTimestamp}} +- interfacePrefix: {{interfacePrefix}} +- library: {{library}} +- licenseId: {{licenseId}} +- modelPropertyNaming: {{modelPropertyNaming}} +- netCoreProjectFile: {{netCoreProjectFile}} +- nonPublicApi: {{nonPublicApi}} +- nullableReferenceTypes: {{nullableReferenceTypes}} +- optionalAssemblyInfo: {{optionalAssemblyInfo}} +- optionalEmitDefaultValues: {{optionalEmitDefaultValues}} +- optionalMethodArgument: {{optionalMethodArgument}} +- optionalProjectFile: {{optionalProjectFile}} +- packageGuid: {{packageGuid}} +- packageName: {{packageName}} +- packageTags: {{packageTags}} +- packageVersion: {{packageVersion}} +- releaseNote: {{releaseNote}} +- returnICollection: {{returnICollection}} +- sortParamsByRequiredFlag: {{sortParamsByRequiredFlag}} +- sourceFolder: {{sourceFolder}} +- targetFramework: {{targetFramework}} +- useCollection: {{useCollection}} +- useDateTimeOffset: {{useDateTimeOffset}} +- useOneOfDiscriminatorLookup: {{useOneOfDiscriminatorLookup}} +- validatable: {{validatable}}{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}){{/infoUrl}} + +This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project. \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache new file mode 100644 index 000000000000..8e77b0dcec21 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache @@ -0,0 +1,62 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; + +namespace {{packageName}}.Client +{ + /// + /// The base for all tokens. + /// + public abstract class TokenBase + { + private readonly TimeSpan _tokenTimeOut; + private DateTime _nextAvailable = DateTime.UtcNow; + private object _nextAvailableLock = new(); + private readonly System.Timers.Timer _timer = new(); + + internal delegate void TokenBecameAvailableEventHandler(object sender); + internal event TokenBecameAvailableEventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} TokenBecameAvailable; + + + /// + /// Initialize a TokenBase object. + /// + /// + internal TokenBase(TimeSpan tokenTimeOut) + { + if (tokenTimeOut == null || tokenTimeOut == TimeSpan.MinValue) + throw new ArgumentException("Invalid token timeout value.", nameof(tokenTimeOut)); + + _tokenTimeOut = tokenTimeOut; + + _timer.Interval = _tokenTimeOut.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 sender, System.Timers.ElapsedEventArgs e) + { + if (TokenBecameAvailable != null && !IsRateLimited) + TokenBecameAvailable.Invoke(this); + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache new file mode 100644 index 000000000000..adbb59abc579 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache @@ -0,0 +1,36 @@ +// +{{partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System.Linq; + +namespace {{packageName}}.Client +{ + /// + /// A container for a collection of tokens. + /// + /// + public sealed class TokenContainer where T : TokenBase + { + /// + /// The collection of tokens + /// + public System.Collections.Generic.List Tokens { get; } = new(); + + /// + /// Instantiates a TokenContainer + /// + public TokenContainer() + { + } + + /// + /// Instantiates a TokenContainer + /// + /// + public TokenContainer(System.Collections.Generic.IEnumerable tokens) + { + Tokens = tokens.ToList(); + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache new file mode 100644 index 000000000000..21c493f01b3f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache @@ -0,0 +1,49 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using {{packageName}}.Client; + +namespace {{packageName}} +{ + /// + /// Provides a token to the api clients. + /// + /// + public class TokenProvider where T : TokenBase + { + internal System.Threading.Channels.Channel AvailableTokens { get; } + + private T[] _tokens; + private readonly object _lock = new object(); + + /// + /// Instantiates a TokenProvider + /// + /// + public TokenProvider(TokenContainer container) + { + if (container.Tokens.Count == 0) + throw new ArgumentException("You did not provide any tokens."); + + _tokens = new T[container.Tokens.Count]; + + System.Threading.Channels.BoundedChannelOptions options = new(_tokens.Length) + { + FullMode = System.Threading.Channels.BoundedChannelFullMode.DropWrite + }; + + AvailableTokens = System.Threading.Channels.Channel.CreateBounded(options); + + for (int i = 0; i < container.Tokens.Count; i++) + { + _tokens[i] = container.Tokens[i]; + _tokens[i].TokenBecameAvailable += ((sender) => AvailableTokens.Writer.TryWrite((T) sender)); + } + } + + internal async System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null) + => await AvailableTokens.Reader.ReadAsync(cancellation.GetValueOrDefault()).ConfigureAwait(false); + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache new file mode 100644 index 000000000000..a0bc2edfccb6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -0,0 +1,289 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Mime; +using {{packageName}}.Client; +{{#hasImport}} +using {{packageName}}.{{modelPackage}}; +{{/hasImport}} + +namespace {{packageName}}.{{apiPackage}} +{ + {{#operations}} + /// + /// Represents a collection of functions to interact with the API endpoints + /// + {{>visibility}} interface {{interfacePrefix}}{{classname}} + { + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task<ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> + System.Threading.Tasks.Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}> + System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> + System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + + {{/operation}} + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + {{>visibility}} partial class {{classname}} : {{interfacePrefix}}{{classname}} + { + private readonly System.Net.Http.HttpClient _httpClient;{{#hasApiKeyMethods}} + private readonly TokenProvider<{{packageName}}.Client.ApiKeyToken> _apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + private readonly TokenProvider<{{packageName}}.Client.BearerToken> _bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + private readonly TokenProvider<{{packageName}}.Client.BasicToken> _basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + private readonly TokenProvider<{{packageName}}.Client.HttpSignatureToken> _httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + private readonly TokenProvider<{{packageName}}.Client.OAuthToken> _oauthTokenProvider;{{/hasOAuthMethods}} + + /// + /// Initializes a new instance of the class. + /// + /// + public {{classname}}(System.Net.Http.HttpClient httpClient, {{#hasApiKeyMethods}} + TokenProvider<{{packageName}}.Client.ApiKeyToken> apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}, + TokenProvider<{{packageName}}.Client.BearerToken> bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}}, + TokenProvider<{{packageName}}.Client.BasicToken> basicTokenProvider{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}}, + TokenProvider<{{packageName}}.Client.HttpSignatureToken> httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}, + TokenProvider<{{packageName}}.Client.OAuthToken> oauthTokenProvider{{/hasOAuthMethods}}) + { + _httpClient = httpClient;{{#hasApiKeyMethods}} + _apiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + _bearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + _basicTokenProvider = basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + _httpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + _oauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}} + } + {{#operation}} + + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> + public async System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + { + {{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); + return result.Content ?? throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); + } + + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> + public async System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + { + {{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); + + return result.IsSuccessStatusCode + ? result.Content + : null; + } + + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// Task<ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}>> + public async System.Threading.Tasks.Task<{{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + { + {{#allParams}}{{#-first}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-first}}{{#required}}{{#nullableReferenceTypes}} +{{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} + + if ({{paramName}} == null) + throw new ArgumentNullException(nameof({{paramName}}));{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^vendorExtensions.x-csharp-value-type}} + + if ({{paramName}} == null) + throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{#-last}} + + #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-last}}{{/allParams}} +{{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} + + using System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage(); + + UriBuilder uriBuilder = new UriBuilder(); + uriBuilder.Host = ClientUtils.HOST; + uriBuilder.Scheme = ClientUtils.SCHEME; + uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} + uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} + + if ({{paramName}} != null) + uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; + {{/required}}{{/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}} + +{{! all the redundant tags here are to get the spacing just right }} + {{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + {{/required}}{{/queryParams}}{{#queryParams}}{{#-first}} + {{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null) + parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + + {{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}} + + request.Headers.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + request.Headers.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} + + System.Net.Http.MultipartContent multipartContent = new System.Net.Http.MultipartContent(); + + request.Content = multipartContent;{{#formParams}}{{#-first}} + + List> formParams = new List>(); + + multipartContent.Add(new System.Net.Http.FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}} + + formParams.Add(new KeyValuePair("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}} + + if ({{paramName}} != null) + formParams.Add(new KeyValuePair("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}} + + multipartContent.Add(new System.Net.Http.StreamContent({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + multipartContent.Add(new System.Net.Http.StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} + + if (({{paramName}} as object) is System.IO.Stream stream) + request.Content = new System.Net.Http.StreamContent(stream); + else + request.Content = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, {{packageName}}.Client.ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} + + List tokens = new List();{{/-first}}{{#isApiKey}} + + ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken); + + tokens.Add(apiKey);{{#isKeyInHeader}} + + apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} + + apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} + + apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} + +{{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} + request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} + + BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken); + + tokens.Add(basicToken); + + basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} + + BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken); + + tokens.Add(bearerToken); + + bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} + + OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken); + + tokens.Add(oauthToken); + + oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} + + HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken); + + tokens.Add(signatureToken); + + string requestBody = (await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault())); + + signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} + + string[] contentTypes = new string[] { + {{/-first}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{#-last}} + };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = {{packageName}}.Client.ClientUtils.SelectHeaderContentType(contentTypes); + + if (contentType != null) + request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}} + + string[] accepts = new string[] { {{/-first}}{{/produces}} + {{#produces}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{/produces}}{{#produces}}{{#-last}} + };{{/-last}}{{/produces}}{{#produces}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = {{packageName}}.Client.ClientUtils.SelectHeaderAccept(accepts); + + if (accept != null) + request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(accept)); + {{/-first}}{{/produces}} + request.Method = System.Net.Http.HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; + + using System.Net.Http.HttpResponseMessage responseMessage = await _httpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + + string responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); + + if (apiResponse.IsSuccessStatusCode) + apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, {{packageName}}.Client.ClientUtils.JsonSerializerSettings);{{#authMethods}} + else if (apiResponse.StatusCode == HttpStatusCode.TooManyRequests) + foreach(TokenBase token in tokens) + token.BeginRateLimit();{{/authMethods}} + + return apiResponse; + } + {{/operation}} + } + {{/operations}} +} From 9a21519171eeaccd3ae2ccbf4377f021af10c9a6 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 23 Oct 2021 18:45:34 -0400 Subject: [PATCH 03/35] added an event, improved docs, added logging --- .../languages/CSharpNetCoreClientCodegen.java | 1 + .../generichost/ClientUtils.mustache | 9 + .../libraries/generichost/api.mustache | 323 ++++++++++-------- 3 files changed, 189 insertions(+), 144 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 081fec491c0f..780188ad7132 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -764,6 +764,7 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", clientPackageDir, "OpenAPIDateConverter.cs")); + supportingFiles.add(new SupportingFile("ApiResponseEventArgs.mustache", clientPackageDir, "ApiResponseEventArgs.cs")); if (Boolean.FALSE.equals(excludeTests.get())) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index badf8272117c..4348647bda48 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -41,6 +41,15 @@ namespace {{packageName}}.Client } {{/useCompareNetObjects}} + /// + /// A delegate for events. + /// + /// + /// + /// + /// + public delegate void EventHandler(object sender, T e) where T : EventArgs; + /// /// Custom JSON serializer /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index a0bc2edfccb6..484dd2665ff3 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -4,10 +4,11 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using System.Net; -using System.Net.Mime; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System.Net.Http; +using System.Net.Http.Headers; using {{packageName}}.Client; {{#hasImport}} using {{packageName}}.{{modelPackage}}; @@ -21,6 +22,12 @@ namespace {{packageName}}.{{apiPackage}} /// {{>visibility}} interface {{interfacePrefix}}{{classname}} { + /// + /// An event to track the health of the server. + /// If you store these event args, be sure to purge old event args to prevent a memory leak. + /// + event ClientUtils.EventHandler? ApiResponded; + {{#operation}} /// /// {{summary}} @@ -28,13 +35,13 @@ namespace {{packageName}}.{{apiPackage}} /// /// {{notes}} /// - /// Thrown when fails to make API call + /// Thrown when fails to make API call {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task<ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> - System.Threading.Tasks.Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); /// /// {{summary}} @@ -42,13 +49,13 @@ namespace {{packageName}}.{{apiPackage}} /// /// {{notes}} /// - /// Thrown when fails to make API call + /// Thrown when fails to make API call {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}> - System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); /// /// {{summary}} @@ -61,7 +68,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); {{/operation}} } @@ -71,24 +78,32 @@ namespace {{packageName}}.{{apiPackage}} /// {{>visibility}} partial class {{classname}} : {{interfacePrefix}}{{classname}} { - private readonly System.Net.Http.HttpClient _httpClient;{{#hasApiKeyMethods}} - private readonly TokenProvider<{{packageName}}.Client.ApiKeyToken> _apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - private readonly TokenProvider<{{packageName}}.Client.BearerToken> _bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - private readonly TokenProvider<{{packageName}}.Client.BasicToken> _basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - private readonly TokenProvider<{{packageName}}.Client.HttpSignatureToken> _httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - private readonly TokenProvider<{{packageName}}.Client.OAuthToken> _oauthTokenProvider;{{/hasOAuthMethods}} + /// + /// An event to track the health of the server. + /// If you store these event args, be sure to purge old event args to prevent a memory leak. + /// + public event ClientUtils.EventHandler? ApiResponded; + + private readonly ILogger<{{classname}}> _logger; + private readonly HttpClient _httpClient;{{#hasApiKeyMethods}} + private readonly TokenProvider _apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + private readonly TokenProvider _bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + private readonly TokenProvider _basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + private readonly TokenProvider _httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + private readonly TokenProvider _oauthTokenProvider;{{/hasOAuthMethods}} /// /// Initializes a new instance of the class. /// /// - public {{classname}}(System.Net.Http.HttpClient httpClient, {{#hasApiKeyMethods}} - TokenProvider<{{packageName}}.Client.ApiKeyToken> apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}, - TokenProvider<{{packageName}}.Client.BearerToken> bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}}, - TokenProvider<{{packageName}}.Client.BasicToken> basicTokenProvider{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}}, - TokenProvider<{{packageName}}.Client.HttpSignatureToken> httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}, - TokenProvider<{{packageName}}.Client.OAuthToken> oauthTokenProvider{{/hasOAuthMethods}}) + public {{classname}}(ILogger<{{classname}}> logger, HttpClient httpClient, {{#hasApiKeyMethods}} + TokenProvider apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}, + TokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}}, + TokenProvider basicTokenProvider{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}}, + TokenProvider httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}, + TokenProvider oauthTokenProvider{{/hasOAuthMethods}}) { + _logger = logger; _httpClient = httpClient;{{#hasApiKeyMethods}} _apiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} _bearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} @@ -101,32 +116,39 @@ namespace {{packageName}}.{{apiPackage}} /// /// {{summary}} {{notes}} /// - /// Thrown when fails to make API call + /// Thrown when fails to make API call {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. - /// Task<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - public async System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + /// <> + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) { - {{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); return result.Content ?? throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); } /// /// {{summary}} {{notes}} /// - /// Thrown when fails to make API call + /// Thrown when fails to make API call {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. - /// Task<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - public async System.Threading.Tasks.Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + /// <> + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) { - {{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); - - return result.IsSuccessStatusCode + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null; + try + { + result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); + } + catch (Exception) + { + } + + return result != null && result.IsSuccessStatusCode ? result.Content : null; } @@ -134,154 +156,167 @@ namespace {{packageName}}.{{apiPackage}} /// /// {{summary}} {{notes}} /// - /// Thrown when fails to make API call + /// Thrown when fails to make API call {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. - /// Task<ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}>> - public async System.Threading.Tasks.Task<{{packageName}}.Client.ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + /// <> where T : + public async Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) { - {{#allParams}}{{#-first}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-first}}{{#required}}{{#nullableReferenceTypes}} + try + { + {{#allParams}}{{#-first}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-first}}{{#required}}{{#nullableReferenceTypes}} {{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} - if ({{paramName}} == null) - throw new ArgumentNullException(nameof({{paramName}}));{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^vendorExtensions.x-csharp-value-type}} + if ({{paramName}} == null) + throw new ArgumentNullException(nameof({{paramName}}));{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^vendorExtensions.x-csharp-value-type}} + + if ({{paramName}} == null) + throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{#-last}} - if ({{paramName}} == null) - throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{#-last}} - - #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-last}}{{/allParams}} + #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-last}}{{/allParams}} {{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} - using System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage(); + using HttpRequestMessage request = new HttpRequestMessage(); - UriBuilder uriBuilder = new UriBuilder(); - uriBuilder.Host = ClientUtils.HOST; - uriBuilder.Scheme = ClientUtils.SCHEME; - uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} - uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} + UriBuilder uriBuilder = new UriBuilder(); + uriBuilder.Host = ClientUtils.HOST; + uriBuilder.Scheme = ClientUtils.SCHEME; + uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} + uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} - if ({{paramName}} != null) - uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; - {{/required}}{{/pathParams}}{{#queryParams}}{{#-first}} + if ({{paramName}} != null) + uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; + {{/required}}{{/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}} + 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}} {{! all the redundant tags here are to get the spacing just right }} - {{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); - {{/required}}{{/queryParams}}{{#queryParams}}{{#-first}} - {{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null) - parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); - - {{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}} - - request.Headers.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} - - if ({{paramName}} != null) - request.Headers.Add("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} - - System.Net.Http.MultipartContent multipartContent = new System.Net.Http.MultipartContent(); - - request.Content = multipartContent;{{#formParams}}{{#-first}} - - List> formParams = new List>(); - - multipartContent.Add(new System.Net.Http.FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}} - - formParams.Add(new KeyValuePair("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}} - - if ({{paramName}} != null) - formParams.Add(new KeyValuePair("{{baseName}}", {{packageName}}.Client.ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}} - - multipartContent.Add(new System.Net.Http.StreamContent({{paramName}}));{{/required}}{{^required}} - - if ({{paramName}} != null) - multipartContent.Add(new System.Net.Http.StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} + {{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + {{/required}}{{/queryParams}}{{#queryParams}}{{#-first}} + {{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null) + parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + + {{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}} - if (({{paramName}} as object) is System.IO.Stream stream) - request.Content = new System.Net.Http.StreamContent(stream); - else - request.Content = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, {{packageName}}.Client.ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} + request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} + + MultipartContent multipartContent = new MultipartContent(); + + request.Content = multipartContent;{{#formParams}}{{#-first}} + + List> formParams = new List>(); - List tokens = new List();{{/-first}}{{#isApiKey}} + multipartContent.Add(new FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}} + + formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}} + + if ({{paramName}} != null) + formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}} + + multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} + + if (({{paramName}} as object) is System.IO.Stream stream) + request.Content = new StreamContent(stream); + else + request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} + + List tokens = new List();{{/-first}}{{#isApiKey}} + + ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken); + + tokens.Add(apiKey);{{#isKeyInHeader}} + + apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} + + apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} + + apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} - ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken); - - tokens.Add(apiKey);{{#isKeyInHeader}} - - apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} - - apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} - - apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} - {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} - request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} + request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} - BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken); + BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken); - tokens.Add(basicToken); - - basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} + tokens.Add(basicToken); + + basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} - BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken); + BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken); - tokens.Add(bearerToken); - - bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} + tokens.Add(bearerToken); + + bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} - OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken); + OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken); - tokens.Add(oauthToken); - - oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} - - HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken); + tokens.Add(oauthToken); + + oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} + + HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken); - tokens.Add(signatureToken); + tokens.Add(signatureToken); - string requestBody = (await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault())); + string requestBody = (await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault())); - signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} - - string[] contentTypes = new string[] { - {{/-first}}"{{{mediaType}}}"{{^-last}}, - {{/-last}}{{#-last}} - };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} - - string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = {{packageName}}.Client.ClientUtils.SelectHeaderContentType(contentTypes); + signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} + + string[] contentTypes = new string[] { + {{/-first}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{#-last}} + };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = ClientUtils.SelectHeaderContentType(contentTypes); + + if (contentType != null) + request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}} + + string[] accepts = new string[] { {{/-first}}{{/produces}} + {{#produces}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{/produces}}{{#produces}}{{#-last}} + };{{/-last}}{{/produces}}{{#produces}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = ClientUtils.SelectHeaderAccept(accepts); + + if (accept != null) + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + {{/-first}}{{/produces}} + request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; + + using HttpResponseMessage responseMessage = await _httpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); - if (contentType != null) - request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}} - - string[] accepts = new string[] { {{/-first}}{{/produces}} - {{#produces}}"{{{mediaType}}}"{{^-last}}, - {{/-last}}{{/produces}}{{#produces}}{{#-last}} - };{{/-last}}{{/produces}}{{#produces}}{{#-first}} - - string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = {{packageName}}.Client.ClientUtils.SelectHeaderAccept(accepts); + DateTime requestedAt = DateTime.UtcNow; - if (accept != null) - request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(accept)); - {{/-first}}{{/produces}} - request.Method = System.Net.Http.HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; - - using System.Net.Http.HttpResponseMessage responseMessage = await _httpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + string responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); - string responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + if (ApiResponded != null) + ApiResponded.Invoke(this, new ApiResponseEventArgs(requestedAt, DateTime.UtcNow, responseMessage.StatusCode, "{{path}}")); - ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); - if (apiResponse.IsSuccessStatusCode) - apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, {{packageName}}.Client.ClientUtils.JsonSerializerSettings);{{#authMethods}} - else if (apiResponse.StatusCode == HttpStatusCode.TooManyRequests) - foreach(TokenBase token in tokens) - token.BeginRateLimit();{{/authMethods}} + if (apiResponse.IsSuccessStatusCode) + apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, ClientUtils.JsonSerializerSettings);{{#authMethods}} + else if (apiResponse.StatusCode == HttpStatusCode.TooManyRequests) + foreach(TokenBase token in tokens) + token.BeginRateLimit();{{/authMethods}} - return apiResponse; + return apiResponse; + } + catch(Exception e) + { + _logger.LogError(e, "An error occured while sending the request to the server."); + throw; + } } {{/operation}} } From 39af1989cc7285619eec4d0c6d29536090458870 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 23 Oct 2021 18:46:36 -0400 Subject: [PATCH 04/35] adding event args file --- .../generichost/ApiResponseEventArgs.mustache | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponseEventArgs.mustache diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponseEventArgs.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponseEventArgs.mustache new file mode 100644 index 000000000000..5ff16199326e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponseEventArgs.mustache @@ -0,0 +1,47 @@ +using System; +using System.Net; + +namespace {{packageName}}.Client +{ + /// + /// Useful for tracking server health. + /// + public class ApiResponseEventArgs : EventArgs + { + /// + /// The time the request was sent. + /// + public DateTime RequestedAt { get; } + /// + /// The time the response was received. + /// + public DateTime ReceivedAt { get; } + /// + /// The HttpStatusCode received. + /// + public HttpStatusCode HttpStatus { get; } + /// + /// The path requested. + /// + public string Path { get; } + /// + /// The elapsed time from request to response. + /// + public TimeSpan ToTimeSpan => this.ReceivedAt - this.RequestedAt; + + /// + /// The event args used to track server health. + /// + /// + /// + /// + /// + public ApiResponseEventArgs(DateTime requestedAt, DateTime receivedAt, HttpStatusCode httpStatus, string path) + { + RequestedAt = requestedAt; + ReceivedAt = receivedAt; + HttpStatus = httpStatus; + Path = path; + } + } +} From a4a1d4ed213de047d8c7af81f3e9609797480015 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 23 Oct 2021 18:48:40 -0400 Subject: [PATCH 05/35] fixed hard coded package name --- .../libraries/generichost/HostConfiguration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 5106e9cf76e7..2c3d934c4a6f 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; -using CocApi.Api; +using {{packageName}}.Api; namespace {{packageName}}.Client { From 63f291514fc26fadb7b7cf3dcf261a1a178c5bba Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 23 Oct 2021 18:52:39 -0400 Subject: [PATCH 06/35] added an AddTokens overload for a single token --- .../generichost/HostConfiguration.mustache | 13 +++++++++++++ .../libraries/generichost/README.mustache | 3 +-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 2c3d934c4a6f..173263927830 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -73,5 +73,18 @@ namespace {{packageName}}.Client return this; } + + /// + /// Adds tokens to your IServiceCollection + /// + /// + /// + /// + public HostConfiguration AddTokens(T token) where T : TokenBase + { + _services.AddSingleton(new TokenContainer(new T[]{ token })); + + return this; + } } } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index cf2944715368..6939ba3da59a 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -43,8 +43,7 @@ namespace YourProject { // the type of token here depends on the api security specifications ApiKeyToken token = new(TimeSpan.FromSeconds(1), $"Bearer "); - ApiKeyToken[] tokens = new ApiKeyToken[] { token }; - options.AddTokens(tokens); + options.AddTokens(token); options.ConfigureJsonOptions((jsonOptions) => { From a83fc746d08c2221dc601fe1d7dfd7a813d45097 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 23 Oct 2021 19:14:13 -0400 Subject: [PATCH 07/35] changed api clients to singletons to support the event registration --- .../libraries/generichost/HostConfiguration.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 173263927830..a099b4299276 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -32,7 +32,7 @@ namespace {{packageName}}.Client services.AddSingleton>();{{/hasOAuthMethods}} {{/hasAuthMethods}} - {{#apiInfo}}{{#apis}}services.AddTransient<{{interfacePrefix}}{{classname}}, {{classname}}>();{{^-last}} + {{#apiInfo}}{{#apis}}services.AddSingleton<{{interfacePrefix}}{{classname}}, {{classname}}>();{{^-last}} {{/-last}}{{/apis}}{{/apiInfo}} } From 2b5d5a6fd9911ed3938f3ec75e5356c308bf7f45 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sun, 7 Nov 2021 15:56:49 -0500 Subject: [PATCH 08/35] build samples --- docs/generators/csharp-netcore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generators/csharp-netcore.md b/docs/generators/csharp-netcore.md index 7a30e18f899e..d26232e51d04 100644 --- a/docs/generators/csharp-netcore.md +++ b/docs/generators/csharp-netcore.md @@ -13,7 +13,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true| |interfacePrefix|Prefix interfaces with a community standard or widely accepted prefix.| |I| -|library|HTTP library template (sub-template) to use|
**httpclient**
HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.)
**restsharp**
RestSharp (https://github.com/restsharp/RestSharp)
|restsharp| +|library|HTTP library template (sub-template) to use|
**generichost**
HttpClient with Generic Host dependency injection (https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host) (Experimental. Subject to breaking changes without notice.)
**httpclient**
HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. Subject to breaking changes without notice.)
**restsharp**
RestSharp (https://github.com/restsharp/RestSharp)
|restsharp| |licenseId|The identifier of the license| |null| |modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |PascalCase| |netCoreProjectFile|Use the new format (.NET Core) for .NET project files (.csproj).| |false| From 4ea5722a6f0ee6d8d3914ea52b3e899a3990f5f8 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Wed, 24 Nov 2021 11:48:41 -0500 Subject: [PATCH 09/35] log exceptions while executing api responded event --- .../libraries/generichost/api.mustache | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index 484dd2665ff3..cd6b31011f21 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -151,7 +151,7 @@ namespace {{packageName}}.{{apiPackage}} return result != null && result.IsSuccessStatusCode ? result.Content : null; - } + } /// /// {{summary}} {{notes}} @@ -300,7 +300,16 @@ namespace {{packageName}}.{{apiPackage}} string responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); if (ApiResponded != null) - ApiResponded.Invoke(this, new ApiResponseEventArgs(requestedAt, DateTime.UtcNow, responseMessage.StatusCode, "{{path}}")); + { + try + { + ApiResponded.Invoke(this, new ApiResponseEventArgs(requestedAt, DateTime.UtcNow, responseMessage.StatusCode, "{{path}}")); + } + catch(Exception e) + { + _logger.LogError(e, "An error occured while invoking ApiResponded."); + } + } ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); From 1f2937e5befe1f44a7cee8602b2568166e5a7c28 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Wed, 24 Nov 2021 12:06:29 -0500 Subject: [PATCH 10/35] nrt bug fixes, dangling comma fix --- .../libraries/generichost/ClientUtils.mustache | 2 +- .../libraries/generichost/api.mustache | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 4348647bda48..554c974b890f 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -281,7 +281,7 @@ namespace {{packageName}}.Client options(config); } - /// + /// /// Adds a Polly retry policy to your clients. /// /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index cd6b31011f21..b478e38bf9eb 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -26,7 +26,7 @@ namespace {{packageName}}.{{apiPackage}} /// An event to track the health of the server. /// If you store these event args, be sure to purge old event args to prevent a memory leak. /// - event ClientUtils.EventHandler? ApiResponded; + event ClientUtils.EventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded; {{#operation}} /// @@ -41,7 +41,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task<ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> - Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null); /// /// {{summary}} @@ -55,7 +55,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null); /// /// {{summary}} @@ -68,7 +68,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null); {{/operation}} } @@ -82,7 +82,7 @@ namespace {{packageName}}.{{apiPackage}} /// An event to track the health of the server. /// If you store these event args, be sure to purge old event args to prevent a memory leak. /// - public event ClientUtils.EventHandler? ApiResponded; + public event ClientUtils.EventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded; private readonly ILogger<{{classname}}> _logger; private readonly HttpClient _httpClient;{{#hasApiKeyMethods}} @@ -96,7 +96,7 @@ namespace {{packageName}}.{{apiPackage}} /// Initializes a new instance of the class. /// /// - public {{classname}}(ILogger<{{classname}}> logger, HttpClient httpClient, {{#hasApiKeyMethods}} + public {{classname}}(ILogger<{{classname}}> logger, HttpClient httpClient{{#hasApiKeyMethods}}, TokenProvider apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}, TokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}}, TokenProvider basicTokenProvider{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}}, @@ -122,7 +122,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> - public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); return result.Content ?? throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); @@ -137,7 +137,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> - public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null; try @@ -162,7 +162,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> where T : - public async Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken = null) + public async Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { try { From fafe67173a6ce63cc1d82ebb1f9826e8054749f2 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Wed, 24 Nov 2021 19:17:06 -0500 Subject: [PATCH 11/35] resolving comments --- .../languages/CSharpNetCoreClientCodegen.java | 14 +++ .../generichost/ApiKeyToken.mustache | 14 +-- .../generichost/ClientUtils.mustache | 20 ++-- .../generichost/HostConfiguration.mustache | 2 +- .../libraries/generichost/README.mustache | 12 +-- .../libraries/generichost/api.mustache | 12 +-- .../libraries/generichost/api_test.mustache | 92 +++++++++++++++++++ .../csharp-netcore/netcore_project.mustache | 2 + .../netcore_testproject.mustache | 3 +- 9 files changed, 136 insertions(+), 35 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 780188ad7132..1a9eb25ea32d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -573,6 +573,20 @@ public void processOpts() { if (GENERICHOST.equals(getLibrary())){ setLibrary(GENERICHOST); additionalProperties.put("useGenericHost", true); + String projectName = (String) additionalProperties.getOrDefault("projectName", ""); + LOGGER.warn("Project name is " + projectName); + + if (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$"))){ + throw new RuntimeException("a"); + } + + if (!"".equals(projectName) && Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*"))){ + throw new RuntimeException("b"); + } + + if (!"".equals(projectName) && (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$")) || Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*")))){ + throw new RuntimeException("Invalid project name " + projectName + ". May only contain alphanumeric characaters or underscore and start with a letter."); + } } else if (RESTSHARP.equals(getLibrary())) { additionalProperties.put("useRestSharp", true); needsCustomHttpMethod = true; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache index 86292f032f92..77d98ddc8a7d 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache @@ -13,20 +13,17 @@ namespace {{packageName}}.Client { private string _raw; - private bool _useCookieContainer; - /// /// Constructs an ApiKeyToken object. /// /// /// - /// - public ApiKeyToken(TimeSpan tokenTimeOut, string value, bool useCookieContainer = false) : base(tokenTimeOut) + /// + public ApiKeyToken(TimeSpan tokenTimeOut, string value, string prefix = "Bearer ") : base(tokenTimeOut) { - _raw = value; - - _useCookieContainer = useCookieContainer; + _raw = $"{ prefix }{ value }"; } + /// /// Places the token in the cookie. /// @@ -34,8 +31,7 @@ namespace {{packageName}}.Client /// public virtual void UseInCookie(System.Net.Http.HttpRequestMessage request, string cookieName) { - if (!_useCookieContainer) - request.Headers.Add("Cookie", $"{ cookieName }=_raw"); + request.Headers.Add("Cookie", $"{ cookieName }=_raw"); } /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 554c974b890f..765057b6ceaa 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -9,15 +9,13 @@ using System.Text; using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection;{{#supportsRetry}} using Polly.Timeout; using Polly.Extensions.Http; -using Polly; +using Polly;{{/supportsRetry}} using System.Net.Http; -using {{packageName}}.Api; -{{#useCompareNetObjects}} -using KellermanSoftware.CompareNetObjects; -{{/useCompareNetObjects}} +using {{packageName}}.Api;{{#useCompareNetObjects}} +using KellermanSoftware.CompareNetObjects;{{/useCompareNetObjects}} namespace {{packageName}}.Client { @@ -258,7 +256,7 @@ namespace {{packageName}}.Client /// /// /// - public static IHostBuilder Configure{{packageName}}(this IHostBuilder builder, Action options) + public static IHostBuilder Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(this IHostBuilder builder, Action options) { builder.ConfigureServices((context, services) => { @@ -275,11 +273,11 @@ namespace {{packageName}}.Client /// /// /// - public static void Add{{packageName}}(this IServiceCollection services, Action options) + public static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(this IServiceCollection services, Action options) { HostConfiguration config = new(services); options(config); - } + }{{#supportsRetry}} /// /// Adds a Polly retry policy to your clients. @@ -331,7 +329,7 @@ namespace {{packageName}}.Client => Policy.TimeoutAsync(timeout); private static Polly.CircuitBreaker.AsyncCircuitBreakerPolicy CircuitBreakerPolicy( - PolicyBuilder builder, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) - => builder.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking, durationOfBreak); + PolicyBuilder builder, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) + => builder.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking, durationOfBreak);{{/supportsRetry}} } } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index a099b4299276..957a8891d8d7 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -78,7 +78,7 @@ namespace {{packageName}}.Client /// Adds tokens to your IServiceCollection /// /// - /// + /// /// public HostConfiguration AddTokens(T token) where T : TokenBase { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index 6939ba3da59a..861443f0a45b 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -33,16 +33,16 @@ namespace YourProject { var host = CreateHostBuilder(args).Build(); - var api = host.Services.GetRequiredService<{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#-first}}{{#operation}}{{operationId}}>(); + var api = host.Services.GetRequiredService<{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#-first}}{{#operation}}{{#-first}}{{operationId}}>(); - ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} + ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/-first}}{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .Configure{{packageName}}((context, options) => + .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { // the type of token here depends on the api security specifications - ApiKeyToken token = new(TimeSpan.FromSeconds(1), $"Bearer "); + ApiKeyToken token = new(TimeSpan.FromSeconds(1), $""); options.AddTokens(token); options.ConfigureJsonOptions((jsonOptions) => @@ -51,10 +51,10 @@ namespace YourProject jsonOptions.Converters.Add(new JsonStringEnumConverter()); }); - options.ConfigureClients(client => client + options.ConfigureClients(client => {{#supportsRetry}}client .AddRetryPolicy(2) .AddTimeoutPolicy(TimeSpan.FromSeconds(5)) - .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)) + .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)){{/supportsRetry}} // add whatever middleware you prefer ); }); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index b478e38bf9eb..4207c6715935 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -166,19 +166,17 @@ namespace {{packageName}}.{{apiPackage}} { try { - {{#allParams}}{{#-first}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-first}}{{#required}}{{#nullableReferenceTypes}} -{{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} + {{#hasRequiredParams}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/hasRequiredParams}}{{#allParams}}{{#required}}{{#nullableReferenceTypes}} if ({{paramName}} == null) throw new ArgumentNullException(nameof({{paramName}}));{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^vendorExtensions.x-csharp-value-type}} if ({{paramName}} == null) - throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{#-last}} + throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{/allParams}}{{#hasRequiredParams}} + + #pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/-last}}{{/allParams}} -{{! TODO: add a collection of allRequiredParams so the above does not occur if all parameters are optional }} - - using HttpRequestMessage request = new HttpRequestMessage(); + {{/hasRequiredParams}}using HttpRequestMessage request = new HttpRequestMessage(); UriBuilder uriBuilder = new UriBuilder(); uriBuilder.Host = ClientUtils.HOST; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache new file mode 100644 index 000000000000..2dde04a9b637 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache @@ -0,0 +1,92 @@ +{{>partial_header}} +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +{{#useRestSharp}} +using RestSharp; +{{/useRestSharp}} +using Xunit; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; + +using {{packageName}}.Client; +using {{packageName}}.{{apiPackage}}; +{{#hasImport}} +using {{packageName}}.{{modelPackage}}; +{{/hasImport}} + +namespace {{packageName}}.Test.Api +{ + /// + /// Class for testing {{classname}} + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public sealed class {{classname}}Tests : IDisposable + { + private readonly {{interfacePrefix}}{{classname}} _instance; + + public {{classname}}Tests() + { + var host = CreateHostBuilder(Array.Empty()).Build(); + _instance = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new(TimeSpan.FromSeconds(1), $""); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new(TimeSpan.FromSeconds(1), $""); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new(TimeSpan.FromSeconds(1), "", ""); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new(todo); + HttpSignatureToken httpSignatureToken = new(TimeSpan.FromSeconds(1), config); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new(TimeSpan.FromSeconds(1), "token"); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + }); + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of {{classname}} + /// + [Fact] + public void {{operationId}}InstanceTest() + { + // TODO uncomment below to test 'IsType' {{classname}} + //Assert.IsType<{{classname}}>(_instance); + } + {{#operations}} + {{#operation}} + + /// + /// Test {{operationId}} + /// + [Fact] + public async Task {{operationId}}AsyncTest() + { + // TODO uncomment below to test the method and replace null with proper value + {{#allParams}} + //{{{dataType}}} {{paramName}} = null; + {{/allParams}} + //{{#returnType}}var response = await {{/returnType}}_instance.{{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + {{#returnType}} + //Assert.IsType<{{{.}}}>(response); + {{/returnType}} + } + {{/operation}} + {{/operations}} + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache index a32516a2f035..4aa807c1a019 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache @@ -36,7 +36,9 @@ {{#useGenericHost}} + {{#supportsRetry}} + {{/supportsRetry}} {{/useGenericHost}} {{#supportsRetry}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_testproject.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_testproject.mustache index b20a94c76c1a..d01627884333 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_testproject.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_testproject.mustache @@ -4,7 +4,8 @@ {{testPackageName}} {{testPackageName}} {{testTargetFramework}} - false + false{{#nullableReferenceTypes}} + annotations{{/nullableReferenceTypes}} From c7f6fe6ef6abb723c312a93f9754ad9885d8b1f2 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Wed, 24 Nov 2021 19:34:10 -0500 Subject: [PATCH 12/35] removed debugging lines --- .../languages/CSharpNetCoreClientCodegen.java | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 1a9eb25ea32d..36da56fc8859 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -570,23 +570,14 @@ public void processOpts() { clientPackage = "Client"; + String projectName = (String) additionalProperties.getOrDefault("projectName", ""); + if (!"".equals(projectName) && (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$")) || Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*")))){ + throw new RuntimeException("Invalid project name " + projectName + ". May only contain alphanumeric characaters or underscore and start with a letter."); + } + if (GENERICHOST.equals(getLibrary())){ setLibrary(GENERICHOST); additionalProperties.put("useGenericHost", true); - String projectName = (String) additionalProperties.getOrDefault("projectName", ""); - LOGGER.warn("Project name is " + projectName); - - if (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$"))){ - throw new RuntimeException("a"); - } - - if (!"".equals(projectName) && Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*"))){ - throw new RuntimeException("b"); - } - - if (!"".equals(projectName) && (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$")) || Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*")))){ - throw new RuntimeException("Invalid project name " + projectName + ". May only contain alphanumeric characaters or underscore and start with a letter."); - } } else if (RESTSHARP.equals(getLibrary())) { additionalProperties.put("useRestSharp", true); needsCustomHttpMethod = true; From 77ea9e85899123febf7e864ac119591537351b0c Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 4 Dec 2021 00:48:48 -0500 Subject: [PATCH 13/35] refactored token provider --- .../languages/CSharpNetCoreClientCodegen.java | 3 +- .../generichost/ApiKeyToken.mustache | 4 +- .../libraries/generichost/BasicToken.mustache | 4 +- .../generichost/BearerToken.mustache | 4 +- .../generichost/HostConfiguration.mustache | 34 +++---- .../HttpSigningConfiguration.mustache | 98 +++++++++---------- .../generichost/HttpSigningToken.mustache | 4 +- .../libraries/generichost/OAuthToken.mustache | 4 +- .../libraries/generichost/README.mustache | 4 +- .../generichost/RateLimitProvider`1.mustache | 43 ++++++++ .../libraries/generichost/TokenBase.mustache | 33 ++++--- .../generichost/TokenContainer`1.mustache | 8 +- .../generichost/TokenProvider`1.mustache | 45 +++------ .../libraries/generichost/api.mustache | 4 +- 14 files changed, 166 insertions(+), 126 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 36da56fc8859..b9be1da69d98 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -755,6 +755,7 @@ public void addRestSharpSupportingFiles(final String clientPackageDir, final Str public void addGenericHostSupportingFiles(final String clientPackageDir, final String packageFolder, final AtomicReference excludeTests, final String testPackageFolder, final String testPackageName, final String modelPackageDir){ supportingFiles.add(new SupportingFile("TokenProvider`1.mustache", clientPackageDir, "TokenProvider`1.cs")); + supportingFiles.add(new SupportingFile("RateLimitProvider`1.mustache", clientPackageDir, "RateLimitProvider`1.cs")); supportingFiles.add(new SupportingFile("TokenContainer`1.mustache", clientPackageDir, "TokenContainer`1.cs")); supportingFiles.add(new SupportingFile("TokenBase.mustache", clientPackageDir, "TokenBase.cs")); supportingFiles.add(new SupportingFile("ApiException.mustache", clientPackageDir, "ApiException.cs")); @@ -767,7 +768,7 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); supportingFiles.add(new SupportingFile("netcore_project.mustache", packageFolder, packageName + ".csproj")); supportingFiles.add(new SupportingFile("appveyor.mustache", "", "appveyor.yml")); - supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); + supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", clientPackageDir, "OpenAPIDateConverter.cs")); supportingFiles.add(new SupportingFile("ApiResponseEventArgs.mustache", clientPackageDir, "ApiResponseEventArgs.cs")); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache index 77d98ddc8a7d..98434c3b25a2 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiKeyToken.mustache @@ -16,10 +16,10 @@ namespace {{packageName}}.Client /// /// Constructs an ApiKeyToken object. /// - /// /// /// - public ApiKeyToken(TimeSpan tokenTimeOut, string value, string prefix = "Bearer ") : base(tokenTimeOut) + /// + public ApiKeyToken(string value, string prefix = "Bearer ", TimeSpan? timeout = null) : base(timeout) { _raw = $"{ prefix }{ value }"; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache index 822ce728945c..78fce754c171 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BasicToken.mustache @@ -21,10 +21,10 @@ namespace {{packageName}}.Client /// /// Constructs a BasicToken object. /// - /// /// /// - public BasicToken(TimeSpan tokenTimeOut, string username, string password) : base(tokenTimeOut) + /// + public BasicToken(string username, string password, TimeSpan? timeout = null) : base(timeout) { _username = username; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache index 887b36439c37..0e6a8dbdf231 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/BearerToken.mustache @@ -19,9 +19,9 @@ namespace {{packageName}}.Client /// /// Constructs a BearerToken object. /// - /// /// - public BearerToken(TimeSpan tokenTimeOut, string value) : base(tokenTimeOut) + /// + public BearerToken(string value, TimeSpan? timeout = null) : base(timeout) { _raw = value; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 957a8891d8d7..1ee7dfd17b75 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -25,13 +25,6 @@ namespace {{packageName}}.Client {{#apiInfo}}{{#apis}}_clients.Add(services.AddHttpClient<{{interfacePrefix}}{{classname}}, {{classname}}>(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)));{{^-last}} {{/-last}}{{/apis}}{{/apiInfo}} - {{#hasAuthMethods}}{{#hasApiKeyMethods}}services.AddSingleton>();{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - services.AddSingleton>();{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - services.AddSingleton>();{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - services.AddSingleton>();{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - services.AddSingleton>();{{/hasOAuthMethods}} - - {{/hasAuthMethods}} {{#apiInfo}}{{#apis}}services.AddSingleton<{{interfacePrefix}}{{classname}}, {{classname}}>();{{^-last}} {{/-last}}{{/apis}}{{/apiInfo}} } @@ -64,25 +57,32 @@ namespace {{packageName}}.Client /// /// Adds tokens to your IServiceCollection /// - /// - /// + /// + /// + /// /// - public HostConfiguration AddTokens(IEnumerable tokens) where T : TokenBase + public HostConfiguration AddTokens(TTokenBase token) + where TTokenProvider : TokenProvider + where TTokenBase : TokenBase { - _services.AddSingleton(new TokenContainer(tokens)); - - return this; + return AddTokens(new TTokenBase[]{ token }); } /// /// Adds tokens to your IServiceCollection /// - /// - /// + /// + /// + /// /// - public HostConfiguration AddTokens(T token) where T : TokenBase + public HostConfiguration AddTokens(IEnumerable tokens) + where TTokenProvider : TokenProvider + where TTokenBase : TokenBase { - _services.AddSingleton(new TokenContainer(new T[]{ token })); + TokenContainer container = new TokenContainer(tokens); + _services.AddSingleton>(services => container); + _services.AddSingleton(); + _services.AddSingleton>(services => services.GetRequiredService()); return this; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache index 2f93e5fba74f..23b2d9149a1d 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningConfiguration.mustache @@ -120,8 +120,8 @@ namespace {{packageName}}.Client var httpSignatureHeader = new Dictionary(); - if (HttpSigningHeader.Count == 0) - HttpSigningHeader.Add("(created)"); + if (HttpSigningHeader.Count == 0) + HttpSigningHeader.Add("(created)"); var dateTime = DateTime.Now; string digest = String.Empty; @@ -136,12 +136,12 @@ namespace {{packageName}}.Client var bodyDigest = GetStringHash(HashAlgorithm.ToString(), requestBody); digest = string.Format("SHA-512={0}", Convert.ToBase64String(bodyDigest)); } - else + 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()); + 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); @@ -158,8 +158,8 @@ namespace {{packageName}}.Client 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_CREATED)) + httpSignatureHeader.Add(header.ToLower(), GetUnixTime(dateTime).ToString()); else if (header.Equals(HEADER_DIGEST)) { HttpSignedRequestHeader.Add(HEADER_DIGEST, digest); @@ -178,15 +178,15 @@ namespace {{packageName}}.Client } } - if (!isHeaderFound) - throw new Exception(string.Format("Cannot sign HTTP request.Request does not contain the {0} header.",header)); + 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)); + foreach (var keyVal in httpSignatureHeader) + headerValuesList.Add(string.Format("{0}: {1}", keyVal.Key, keyVal.Value)); //Concatinate headers value separated by new line var headerValuesString = string.Join("\n", headerValuesList); @@ -197,7 +197,7 @@ namespace {{packageName}}.Client if (keyType == PrivateKeyType.RSA) headerSignatureStr = GetRSASignature(signatureStringHash); - else if (keyType == PrivateKeyType.ECDSA) + else if (keyType == PrivateKeyType.ECDSA) headerSignatureStr = GetECDSASignature(signatureStringHash); var cryptographicScheme = "hs2019"; @@ -205,10 +205,10 @@ namespace {{packageName}}.Client KeyId, cryptographicScheme); if (httpSignatureHeader.ContainsKey(HEADER_CREATED)) - authorizationHeaderValue += string.Format(",created={0}", httpSignatureHeader[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(",expires={0}", httpSignatureHeader[HEADER_EXPIRES]); authorizationHeaderValue += string.Format(",headers=\"{0}\",signature=\"{1}\"", headersKeysString, headerSignatureStr); @@ -294,8 +294,8 @@ namespace {{packageName}}.Client } finally { - if (unmanagedString != IntPtr.Zero) - Marshal.ZeroFreeBSTR(unmanagedString); + if (unmanagedString != IntPtr.Zero) + Marshal.ZeroFreeBSTR(unmanagedString); } } else @@ -321,11 +321,11 @@ namespace {{packageName}}.Client byte sbytesLength = 32; //S length 0x20 var rBytes = new List(); var sBytes = new List(); - for (int i = 0; i < 32; i++) + for (int i = 0; i < 32; i++) rBytes.Add(signedBytes[i]); - for (int i = 32; i < 64; i++) - sBytes.Add(signedBytes[i]); + for (int i = 32; i < 64; i++) + sBytes.Add(signedBytes[i]); if (rBytes[0] > 0x7F) { @@ -370,19 +370,19 @@ namespace {{packageName}}.Client bool isPrivateKeyFile = true; byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} pemkey = null; - if (!File.Exists(pemfile)) + 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 (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter)) + isPrivateKeyFile = false; if (isPrivateKeyFile) { pemkey = ConvertPrivateKeyToBytes(pemstr, keyPassPharse); - if (pemkey == null) + if (pemkey == null) return null; return DecodeRSAPrivateKey(pemkey); @@ -397,8 +397,8 @@ namespace {{packageName}}.Client String pemstr = instr.Trim(); byte[] binkey; - if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) - return null; + if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) + return null; StringBuilder sb = new StringBuilder(pemstr); sb.Replace(pemprivheader, ""); @@ -419,7 +419,7 @@ namespace {{packageName}}.Client return null; String saltline = str.ReadLine(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}; // TODO: what do we do here if ReadLine is null? - if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,")) + if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,")) return null; String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim(); @@ -427,8 +427,8 @@ namespace {{packageName}}.Client for (int i = 0; i < salt.Length; i++) salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16); - if (!(str.ReadLine() == "")) - return null; + if (!(str.ReadLine() == "")) + return null; //------ remaining b64 data is encrypted RSA key ---- String encryptedstr = str.ReadToEnd(); @@ -467,19 +467,19 @@ namespace {{packageName}}.Client 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 + 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; + return null; twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number + if (twobytes != 0x0102) //version number return null; bt = binr.ReadByte(); - if (bt != 0x00) + if (bt != 0x00) return null; //------ all private key components are Integer sequences ---- @@ -538,13 +538,13 @@ namespace {{packageName}}.Client byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); - if (bt != 0x02) //expect integer + if (bt != 0x02) //expect integer return 0; bt = binr.ReadByte(); - if (bt == 0x81) - count = binr.ReadByte(); // data size in next byte + if (bt == 0x81) + count = binr.ReadByte(); // data size in next byte else if (bt == 0x82) { highbyte = binr.ReadByte(); // data size in next 2 bytes @@ -552,10 +552,10 @@ namespace {{packageName}}.Client byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } - else + else count = bt; // we already have the data size - while (binr.ReadByte() == 0x00) + while (binr.ReadByte() == 0x00) //remove high order zeros in data count -= 1; @@ -589,8 +589,8 @@ namespace {{packageName}}.Client for (int j = 0; j < miter; j++) { // ---- Now hash consecutively for count times ------ - if (j == 0) - result = data00; //initialize + if (j == 0) + result = data00; //initialize else { Array.Copy(result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, hashtarget, result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.Length); // TODO: what do we do if result is null here? @@ -641,8 +641,8 @@ namespace {{packageName}}.Client /// private PrivateKeyType GetKeyType(string keyFilePath) { - if (!File.Exists(keyFilePath)) - throw new Exception("Key file path does not exist."); + 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"; @@ -653,9 +653,9 @@ namespace {{packageName}}.Client 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)) + 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)) @@ -666,7 +666,7 @@ namespace {{packageName}}.Client //TODO :- update the key based on oid keyType = PrivateKeyType.ECDSA; } - else + else throw new Exception("Either the key is invalid or key is not supported"); return keyType; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache index af97f5b17156..3b47eb30ed2b 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache @@ -19,9 +19,9 @@ namespace {{packageName}}.Client /// /// Constructs an HttpSignatureToken object. /// - /// /// - public HttpSignatureToken(TimeSpan tokenTimeOut, HttpSigningConfiguration configuration) : base(tokenTimeOut) + /// + public HttpSignatureToken(HttpSigningConfiguration configuration, TimeSpan? timeout = null) : base(timeout) { _configuration = configuration; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache index eca08a92f07f..d15a01cf9d3f 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/OAuthToken.mustache @@ -19,9 +19,9 @@ namespace {{packageName}}.Client /// /// Consturcts an OAuthToken object. /// - /// /// - public OAuthToken(TimeSpan tokenTimeOut, string value) : base(tokenTimeOut) + /// + public OAuthToken(string value, TimeSpan? timeout = null) : base(timeout) { _raw = value; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index 861443f0a45b..393f448cb0da 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -42,8 +42,8 @@ namespace YourProject .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { // the type of token here depends on the api security specifications - ApiKeyToken token = new(TimeSpan.FromSeconds(1), $""); - options.AddTokens(token); + ApiKeyToken token = new(""); + options.AddTokens, ApiKeyToken>(token); options.ConfigureJsonOptions((jsonOptions) => { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache new file mode 100644 index 000000000000..37d85bf6b406 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache @@ -0,0 +1,43 @@ +// +{{>partial_header}} +{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} + +using System; +using System.Linq; +using System.Collections.Generic; +using {{packageName}}.Client; + +namespace {{packageName}} +{ + /// + /// Provides a token to the api clients. Tokens will be rate limited based on the provided TimeSpan. + /// + /// + public class RateLimitProvider : TokenProvider where TTokenBase : TokenBase + { + internal System.Threading.Channels.Channel AvailableTokens { get; } + + /// + /// Instantiates a ThrottledTokenProvider. Your tokens will be rate limited based on the token's timeout. + /// + /// + public RateLimitProvider(TokenContainer container) : base(container.Tokens) + { + foreach(TTokenBase token in _tokens) + token.StartTimer(token.Timeout ?? TimeSpan.FromMilliseconds(40)); + + System.Threading.Channels.BoundedChannelOptions options = new(_tokens.Length) + { + FullMode = System.Threading.Channels.BoundedChannelFullMode.DropWrite + }; + + AvailableTokens = System.Threading.Channels.Channel.CreateBounded(options); + + for (int i = 0; i < _tokens.Length; i++) + _tokens[i].TokenBecameAvailable += ((sender) => AvailableTokens.Writer.TryWrite((TTokenBase) sender)); + } + + internal override async System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null) + => await AvailableTokens.Reader.ReadAsync(cancellation.GetValueOrDefault()).ConfigureAwait(false); + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache index 8e77b0dcec21..7a2aa0ba3135 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache @@ -11,11 +11,12 @@ namespace {{packageName}}.Client /// public abstract class TokenBase { - private readonly TimeSpan _tokenTimeOut; private DateTime _nextAvailable = DateTime.UtcNow; private object _nextAvailableLock = new(); private readonly System.Timers.Timer _timer = new(); + + internal TimeSpan? Timeout { get; set; } internal delegate void TokenBecameAvailableEventHandler(object sender); internal event TokenBecameAvailableEventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} TokenBecameAvailable; @@ -23,34 +24,42 @@ namespace {{packageName}}.Client /// /// Initialize a TokenBase object. /// - /// - internal TokenBase(TimeSpan tokenTimeOut) + /// + internal TokenBase(TimeSpan? timeout = null) { - if (tokenTimeOut == null || tokenTimeOut == TimeSpan.MinValue) - throw new ArgumentException("Invalid token timeout value.", nameof(tokenTimeOut)); - - _tokenTimeOut = tokenTimeOut; + Timeout = timeout; - _timer.Interval = _tokenTimeOut.TotalMilliseconds; + 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\ + /// 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); + lock(_nextAvailableLock) + _nextAvailable = DateTime.UtcNow.AddSeconds(5); } private void OnTimer(object sender, System.Timers.ElapsedEventArgs e) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache index adbb59abc579..fb3018a91628 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache @@ -9,13 +9,13 @@ namespace {{packageName}}.Client /// /// A container for a collection of tokens. /// - /// - public sealed class TokenContainer where T : TokenBase + /// + public sealed class TokenContainer where TTokenBase : TokenBase { /// /// The collection of tokens /// - public System.Collections.Generic.List Tokens { get; } = new(); + public System.Collections.Generic.List Tokens { get; } = new(); /// /// Instantiates a TokenContainer @@ -28,7 +28,7 @@ namespace {{packageName}}.Client /// Instantiates a TokenContainer /// /// - public TokenContainer(System.Collections.Generic.IEnumerable tokens) + public TokenContainer(System.Collections.Generic.IEnumerable tokens) { Tokens = tokens.ToList(); } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache index 21c493f01b3f..d8b467a30bf7 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenProvider`1.mustache @@ -3,47 +3,34 @@ {{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} using System; +using System.Linq; +using System.Collections.Generic; using {{packageName}}.Client; namespace {{packageName}} { /// - /// Provides a token to the api clients. + /// A class which will provide tokens. /// - /// - public class TokenProvider where T : TokenBase + public abstract class TokenProvider where TTokenBase : TokenBase { - internal System.Threading.Channels.Channel AvailableTokens { get; } + /// + /// The array of tokens. + /// + protected TTokenBase[] _tokens; - private T[] _tokens; - private readonly object _lock = new object(); + internal abstract System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null); /// - /// Instantiates a TokenProvider + /// Instantiates a TokenProvider. /// - /// - public TokenProvider(TokenContainer container) + /// + public TokenProvider(IEnumerable tokens) { - if (container.Tokens.Count == 0) - throw new ArgumentException("You did not provide any tokens."); - - _tokens = new T[container.Tokens.Count]; + _tokens = tokens.ToArray(); - System.Threading.Channels.BoundedChannelOptions options = new(_tokens.Length) - { - FullMode = System.Threading.Channels.BoundedChannelFullMode.DropWrite - }; - - AvailableTokens = System.Threading.Channels.Channel.CreateBounded(options); - - for (int i = 0; i < container.Tokens.Count; i++) - { - _tokens[i] = container.Tokens[i]; - _tokens[i].TokenBecameAvailable += ((sender) => AvailableTokens.Writer.TryWrite((T) sender)); - } + if (_tokens.Length == 0) + throw new ArgumentException("You did not provide any tokens."); } - - internal async System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null) - => await AvailableTokens.Reader.ReadAsync(cancellation.GetValueOrDefault()).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index 4207c6715935..0cfa1a5f550d 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -184,7 +184,7 @@ namespace {{packageName}}.{{apiPackage}} uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} - if ({{paramName}} != null) + if ({{paramName}} != null) uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; {{/required}}{{/pathParams}}{{#queryParams}}{{#-first}} @@ -202,7 +202,7 @@ namespace {{packageName}}.{{apiPackage}} request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} - if ({{paramName}} != null) + if ({{paramName}} != null) request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} MultipartContent multipartContent = new MultipartContent(); From 641a7b93e39837981f2b8858224f280405123cb5 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 4 Dec 2021 17:15:08 -0500 Subject: [PATCH 14/35] rate limit provider now default --- .../generichost/ClientUtils.mustache | 19 ++++++++++++++ .../generichost/HostConfiguration.mustache | 26 ++++++++++++------- .../libraries/generichost/README.mustache | 5 +++- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 765057b6ceaa..24e32b4f6aae 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -263,6 +263,25 @@ namespace {{packageName}}.Client HostConfiguration config = new(services); options(context, config); + + // ensure that a token provider was provided for this token type + // if not, default to RateLimitProvider + var containerServices = services.Where(s => s.ServiceType.IsGenericType && + s.ServiceType.GetGenericTypeDefinition().IsAssignableTo(typeof(TokenContainer<>))).ToArray(); + + foreach(var containerService in containerServices) + { + var tokenType = containerService.ServiceType.GenericTypeArguments[0]; + + var provider = services.FirstOrDefault(s => s.ServiceType.IsAssignableTo(typeof(TokenProvider<>).MakeGenericType(tokenType))); + + if (provider == null) + { + services.AddSingleton(typeof(RateLimitProvider<>).MakeGenericType(tokenType)); + services.AddSingleton(typeof(TokenProvider<>).MakeGenericType(tokenType), + services => services.GetRequiredService(typeof(RateLimitProvider<>).MakeGenericType(tokenType))); + } + } }); return builder; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 1ee7dfd17b75..5c8d1c06d198 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -57,30 +57,38 @@ namespace {{packageName}}.Client /// /// Adds tokens to your IServiceCollection /// - /// /// /// /// - public HostConfiguration AddTokens(TTokenBase token) - where TTokenProvider : TokenProvider - where TTokenBase : TokenBase + public HostConfiguration UseTokens(TTokenBase token) where TTokenBase : TokenBase { - return AddTokens(new TTokenBase[]{ token }); + return UseTokens(new TTokenBase[]{ token }); } /// /// Adds tokens to your IServiceCollection /// - /// /// /// /// - public HostConfiguration AddTokens(IEnumerable tokens) - where TTokenProvider : TokenProvider - where TTokenBase : TokenBase + public HostConfiguration UseTokens(IEnumerable tokens) where TTokenBase : TokenBase { TokenContainer container = new TokenContainer(tokens); _services.AddSingleton>(services => container); + + return this; + } + + /// + /// Adds a token provider to your IServiceCollection + /// + /// + /// + /// + public HostConfiguration UseProvider() + where TTokenProvider : TokenProvider + where TTokenBase : TokenBase + { _services.AddSingleton(); _services.AddSingleton>(services => services.GetRequiredService()); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index 393f448cb0da..ac3a989e484a 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -43,7 +43,10 @@ namespace YourProject { // the type of token here depends on the api security specifications ApiKeyToken token = new(""); - options.AddTokens, ApiKeyToken>(token); + options.UseTokens(token); + + // optionally choose the method the tokens will be provided with, default is RateLimitProvider + options.UseProvider, ApiKeyToken>(); options.ConfigureJsonOptions((jsonOptions) => { From b9099443fd862612c3fc6654712ed70db67abe55 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sun, 19 Dec 2021 18:55:53 -0500 Subject: [PATCH 15/35] updated readme, added ConfigureAwait(false) --- .../libraries/generichost/README.mustache | 99 ++++++++++--------- .../libraries/generichost/api.mustache | 12 +-- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index ac3a989e484a..ae70abbdf69b 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -3,20 +3,37 @@ ## Run the following powershell command to generate the library -``` +```ps1 +$properties = @( + 'projectName={{projectName}}' + 'packageName={{packageName}}', + 'targetFramework={{targetFramework}}' + 'validatable={{validatable}}', + 'nullableReferenceTypes={{nullableReferenceTypes}}', + 'hideGenerationTimestamp={{hideGenerationTimestamp}}', + 'packageVersion={{packageVersion}}' +) -join "," + +$global = @( + 'apiDocs={{generateApiDocs}}' + 'modelDocs={{generateModelDocs}}', + 'apiTests={{generateApiTests}}', + 'modelTests={{generateModelTests}}', +) -join "," + java -jar "/openapi-generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar" generate ` -g csharp-netcore ` --library generichost ` -i .yml ` -o ` - --additional-properties="packageName=YourPackageName,targetFramework=net5.0,validatable=false,nullableReferenceTypes=true" + --additional-properties $properties ` + --global-property $global ``` ## Using the library in your project -``` -cs +```cs using System; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -31,9 +48,9 @@ namespace YourProject { public static async Task Main(string[] args) { - var host = CreateHostBuilder(args).Build(); + var host = CreateHostBuilder(args).Build();{{#apiInfo}}{{#apis}}{{#-first}} - var api = host.Services.GetRequiredService<{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#-first}}{{#operation}}{{#-first}}{{operationId}}>(); + var api = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();{{#operations}}{{#-first}}{{#operation}}{{#-first}} ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/-first}}{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} } @@ -41,17 +58,16 @@ namespace YourProject public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { - // the type of token here depends on the api security specifications + {{#authMethods}}// the type of token here depends on the api security specifications ApiKeyToken token = new(""); options.UseTokens(token); // optionally choose the method the tokens will be provided with, default is RateLimitProvider options.UseProvider, ApiKeyToken>(); - options.ConfigureJsonOptions((jsonOptions) => + {{/authMethods}}options.ConfigureJsonOptions((jsonOptions) => { - jsonOptions.Converters.Add(new DateTimeOffsetJsonConverter()); - jsonOptions.Converters.Add(new JsonStringEnumConverter()); + // your custom converters if any }); options.ConfigureClients(client => {{#supportsRetry}}client @@ -68,9 +84,10 @@ namespace YourProject ## Questions - What about HttpRequest failures and retries? - You can add Polly or any other middleware in the ConfigureClients method. -- What about rate limits? - A token has a TimeSpan which controls how fast it will be reused. + If supportsRetry is enabled, you can configure Polly in the ConfigureClients method. +- 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. @@ -80,22 +97,19 @@ namespace YourProject ## Notes - If you are not using .Net Standard, consider setting validatable to false in your generator-config.json due to this bug: https://github.com/dotnet/project-system/issues/3934 -- If you want to override the provided mustache templates, provide generate the library with the -t parameter and the folder name which stores your templates.{{#netStandard}} - - -## Frameworks supported - -- .NET Core >=1.0 -- .NET Framework >=4.6 -- Mono/Xamarin >=vNext{{/netStandard}} +- If you want to override the provided mustache templates, provide generate the library with the -t parameter and the folder name which stores your templates. ## Dependencies -- [Json.NET](https://www.nuget.org/packages/Newtonsoft.Json/) - 12.0.3 or later +- [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting/) - 5.0.0 or later +- [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http/) - 5.0.0 or later{{#supportsRetry}} +- [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly/) - 5.0.1 or later +- [Polly](https://www.nuget.org/packages/Polly/) - 7.2.2 or later{{/supportsRetry}} +- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/) - 12.0.3 or later - [JsonSubTypes](https://www.nuget.org/packages/JsonSubTypes/) - 1.7.0 or later{{#useCompareNetObjects}} - [CompareNETObjects](https://www.nuget.org/packages/CompareNETObjects) - 4.61.0 or later{{/useCompareNetObjects}}{{#validatable}} -- [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 4.7.0 or later{{/validatable}} +- [System.ComponentModel.Annotations](https://www.nuget.org/packages/System.ComponentModel.Annotations) - 4.7.0 or later{{/validatable}}{{#apiDocs}} ## Documentation for API Endpoints @@ -104,13 +118,13 @@ All URIs are relative to *{{{basePath}}}* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | -------------{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} -*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}{{/apiDocs}}{{#modelDocs}} ## Documentation for Models {{#modelPackage}}{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md){{/model}}{{/models}}{{/modelPackage}} -{{^modelPackage}}No model defined in this package{{/modelPackage}} +{{^modelPackage}}No model defined in this package{{/modelPackage}}{{/modelDocs}} ## Documentation for Authorization @@ -131,36 +145,32 @@ Class | Method | HTTP request | Description - **Scopes**: {{^scopes}}N/A{{/scopes}}{{#scopes}} - {{scope}}: {{description}}{{/scopes}}{{/isOAuth}}{{/authMethods}} +## Build +- SDK version: {{packageVersion}}{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}}{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} + ## Api Configuration - appName: {{appName}} - appVersion: {{appVersion}} - appDescription: {{appDescription}} -## OpenApi Global properties -- debugOpenAPI: {{debugOpenAPI}} -- debugModels: {{debugModels}} -- debugOperations: {{debugOperations}} -- debugSupportingFiles: {{debugSupportingFiles}} -- verbose: {{verbose}} +## [OpenApi Global properties](https://openapi-generator.tech/docs/globals) - generateAliasAsModel: {{generateAliasAsModel}} -- org.openapitools.codegenutils.oncelogger.enabled: {{org.openapi.tools.codegenutils.oncelogger.enabled}} - supportingFiles: {{supportingFiles}} - models: omitted for brevity - apis: omitted for brevity -- apiDocs: omitted for brevity -- modelDocs: {{modelDocs}} -- apiTests: omitted for brevity -- modelTests: {{modelTest}} +- apiDocs: {{generateApiDocs}} +- modelDocs: {{generateModelDocs}} +- apiTests: {{generateApiTests}} +- modelTests: {{generateModelTests}} - withXml: {{withXml}} -## OpenApi Generator Parameteres -- SDK version: {{packageVersion}}{{^hideGenerationTimestamp}} -- Build date: {{generatedDate}}{{/hideGenerationTimestamp}} -- Build package: {{generatorClass}} -- Package name: {{packageName}} -- allowUnicodeIdentifiers {{allowUnicodeIdentifiers}} -- caseInsensitiveResponseHeaders {{caseInsensitiveResponseHeaders}} -- disallowAdditionalPropertiesIfNotPresent {{disallowAdditionalPropertiesIfNotPresent}} +## [OpenApi Generator Parameteres](https://openapi-generator.tech/docs/generators/csharp-netcore) +- allowUnicodeIdentifiers: {{allowUnicodeIdentifiers}} +- caseInsensitiveResponseHeaders: {{caseInsensitiveResponseHeaders}} +- conditionalSerialization: {{conditionalSerialization}} +- disallowAdditionalPropertiesIfNotPresent: {{disallowAdditionalPropertiesIfNotPresent}} - hideGenerationTimestamp: {{hideGenerationTimestamp}} - interfacePrefix: {{interfacePrefix}} - library: {{library}} @@ -177,6 +187,7 @@ Class | Method | HTTP request | Description - packageName: {{packageName}} - packageTags: {{packageTags}} - packageVersion: {{packageVersion}} +- projectName: {{projectName}} - releaseNote: {{releaseNote}} - returnICollection: {{returnICollection}} - sortParamsByRequiredFlag: {{sortParamsByRequiredFlag}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index 0cfa1a5f550d..bc808ade53e4 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -230,7 +230,7 @@ namespace {{packageName}}.{{apiPackage}} List tokens = new List();{{/-first}}{{#isApiKey}} - ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken); + ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(apiKey);{{#isKeyInHeader}} @@ -243,29 +243,29 @@ namespace {{packageName}}.{{apiPackage}} {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} - BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken); + BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(basicToken); basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} - BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken); + BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(bearerToken); bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} - OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken); + OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(oauthToken); oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} - HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken); + HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(signatureToken); - string requestBody = (await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault())); + string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} From e069979ed1e3385dcd5f3ad68083899414b1a2d3 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Mon, 20 Dec 2021 22:00:57 -0500 Subject: [PATCH 16/35] DI fixes --- .../languages/CSharpNetCoreClientCodegen.java | 3 + .../generichost/ClientUtils.mustache | 48 +++--- .../DependencyInjectionTests.mustache | 140 ++++++++++++++++++ .../generichost/HostConfiguration.mustache | 54 +++++-- .../libraries/generichost/IApi.mustache | 21 +++ .../libraries/generichost/README.mustache | 6 +- .../libraries/generichost/api.mustache | 85 +++++++---- .../libraries/generichost/api_test.mustache | 57 +------ .../generichost/api_test_base.mustache | 41 +++++ 9 files changed, 333 insertions(+), 122 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/IApi.mustache create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index b9be1da69d98..6db5c4ddeef6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -771,9 +771,12 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", clientPackageDir, "OpenAPIDateConverter.cs")); supportingFiles.add(new SupportingFile("ApiResponseEventArgs.mustache", clientPackageDir, "ApiResponseEventArgs.cs")); + supportingFiles.add(new SupportingFile("IApi.mustache", clientPackageDir, getInterfacePrefix() + "Api.cs")); if (Boolean.FALSE.equals(excludeTests.get())) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); + supportingFiles.add(new SupportingFile("api_test_base.mustache", testPackageFolder + File.separator + "Api", "ApiTestsBase.cs")); + supportingFiles.add(new SupportingFile("DependencyInjectionTests.mustache", testPackageFolder + File.separator + "Api", "DependencyInjectionTests.cs")); } if (ProcessUtils.hasHttpSignatureMethods(openAPI)) { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 24e32b4f6aae..b4095b42feca 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -1,12 +1,9 @@ {{>partial_header}} using System; -using System.Collections; -using System.Globalization; using System.IO; using System.Linq; using System.Text; -using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection;{{#supportsRetry}} @@ -264,24 +261,7 @@ namespace {{packageName}}.Client options(context, config); - // ensure that a token provider was provided for this token type - // if not, default to RateLimitProvider - var containerServices = services.Where(s => s.ServiceType.IsGenericType && - s.ServiceType.GetGenericTypeDefinition().IsAssignableTo(typeof(TokenContainer<>))).ToArray(); - - foreach(var containerService in containerServices) - { - var tokenType = containerService.ServiceType.GenericTypeArguments[0]; - - var provider = services.FirstOrDefault(s => s.ServiceType.IsAssignableTo(typeof(TokenProvider<>).MakeGenericType(tokenType))); - - if (provider == null) - { - services.AddSingleton(typeof(RateLimitProvider<>).MakeGenericType(tokenType)); - services.AddSingleton(typeof(TokenProvider<>).MakeGenericType(tokenType), - services => services.GetRequiredService(typeof(RateLimitProvider<>).MakeGenericType(tokenType))); - } - } + Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(services, config); }); return builder; @@ -296,6 +276,32 @@ namespace {{packageName}}.Client { HostConfiguration config = new(services); options(config); + Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(services, config); + } + + private static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(IServiceCollection services, HostConfiguration host) + { + if (!host.HttpClientsAdded) + host.AddCmcHttpClients(); + + // ensure that a token provider was provided for this token type + // if not, default to RateLimitProvider + var containerServices = services.Where(s => s.ServiceType.IsGenericType && + s.ServiceType.GetGenericTypeDefinition().IsAssignableTo(typeof(TokenContainer<>))).ToArray(); + + foreach(var containerService in containerServices) + { + var tokenType = containerService.ServiceType.GenericTypeArguments[0]; + + var provider = services.FirstOrDefault(s => s.ServiceType.IsAssignableTo(typeof(TokenProvider<>).MakeGenericType(tokenType))); + + if (provider == null) + { + services.AddSingleton(typeof(RateLimitProvider<>).MakeGenericType(tokenType)); + services.AddSingleton(typeof(TokenProvider<>).MakeGenericType(tokenType), + services => services.GetRequiredService(typeof(RateLimitProvider<>).MakeGenericType(tokenType))); + } + } }{{#supportsRetry}} /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache new file mode 100644 index 000000000000..376a3899a3e3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache @@ -0,0 +1,140 @@ +{{>partial_header}} +using System; +using Microsoft.Extensions.Hosting; +using {{packageName}}.Client; +using {{packageName}}.{{apiPackage}}; +using Xunit; +using Microsoft.Extensions.DependencyInjection; + +namespace {{packageName}}.Test.Api +{ + /// + /// Tests the dependency injection. + /// + public class DependencyInjectionTest + { + private readonly IHost _hostUsingConfigureWithoutAClient = + Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + }) + .Build(); + + private readonly IHost _hostUsingConfigureWithAClient = + Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); + }) + .Build(); + + private readonly IHost _hostUsingAddWithoutAClient = + Host.CreateDefaultBuilder(Array.Empty()).ConfigureServices((host, services) => + { + services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + }); + }) + .Build(); + + private readonly IHost _hostUsingAddWithAClient = + Host.CreateDefaultBuilder(Array.Empty()).ConfigureServices((host, services) => + { + services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}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.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the configure method + /// + [Fact] + public void ConfigureApiWithoutAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the add method + /// + [Fact] + public void AddApiWithAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the add method + /// + [Fact] + public void AddApiWithoutAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + } +} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 5c8d1c06d198..65117c936d91 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Net.Http; using Microsoft.Extensions.DependencyInjection; using {{packageName}}.Api; @@ -13,7 +15,7 @@ namespace {{packageName}}.Client public class HostConfiguration { private readonly IServiceCollection _services; - private readonly List _clients = new(); + internal bool HttpClientsAdded { get; private set; } /// /// Instantiates the class @@ -21,23 +23,45 @@ namespace {{packageName}}.Client /// public HostConfiguration(IServiceCollection services) { - _services = services; - {{#apiInfo}}{{#apis}}_clients.Add(services.AddHttpClient<{{interfacePrefix}}{{classname}}, {{classname}}>(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)));{{^-last}} - {{/-last}}{{/apis}}{{/apiInfo}} + _services = services;{{#apiInfo}}{{#apis}} + services.AddSingleton<{{interfacePrefix}}{{classname}}, {{classname}}>();{{/apis}}{{/apiInfo}} + } - {{#apiInfo}}{{#apis}}services.AddSingleton<{{interfacePrefix}}{{classname}}, {{classname}}>();{{^-last}} - {{/-last}}{{/apis}}{{/apiInfo}} + /// + /// Configures the HttpClients. + /// + /// + /// + /// + public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}T{{classname}}{{^-last}}, {{/-last}}{{/apis}}> + ( + Action? client = null, Action? builder = null){{#apis}} + where T{{classname}} : class, {{interfacePrefix}}{{classname}}{{/apis}} + { + client ??= c => c.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS); + List builders = new List(); + + {{#apis}}builders.Add(_services.AddHttpClient<{{interfacePrefix}}{{classname}}, T{{classname}}>(client)); + {{/apis}}{{/apiInfo}} + if (builder != null) + foreach (IHttpClientBuilder instance in builders) + builder(instance); + + HttpClientsAdded = true; + + return this; } /// - /// Configures the HttpClients. Use this to add your middleware. + /// Configures the HttpClients. /// - /// + /// + /// /// - public HostConfiguration ConfigureClients(Action clientBuilder) - { - foreach(IHttpClientBuilder client in _clients) - clientBuilder(client); + public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients( + Action? client = null, Action? builder = null) + { + AddCmcHttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); return this; } @@ -60,9 +84,9 @@ namespace {{packageName}}.Client /// /// /// - public HostConfiguration UseTokens(TTokenBase token) where TTokenBase : TokenBase + public HostConfiguration AddTokens(TTokenBase token) where TTokenBase : TokenBase { - return UseTokens(new TTokenBase[]{ token }); + return AddTokens(new TTokenBase[]{ token }); } /// @@ -71,7 +95,7 @@ namespace {{packageName}}.Client /// /// /// - public HostConfiguration UseTokens(IEnumerable tokens) where TTokenBase : TokenBase + public HostConfiguration AddTokens(IEnumerable tokens) where TTokenBase : TokenBase { TokenContainer container = new TokenContainer(tokens); _services.AddSingleton>(services => container); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/IApi.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/IApi.mustache new file mode 100644 index 000000000000..78856d47816c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/IApi.mustache @@ -0,0 +1,21 @@ +using System.Net.Http; + +namespace {{packageName}}.Client +{ + /// + /// Any Api client + /// + public interface {{interfacePrefix}}Api + { + /// + /// The HttpClient + /// + HttpClient HttpClient { get; } + + /// + /// An event to track the health of the server. + /// If you store these event args, be sure to purge old event args to prevent a memory leak. + /// + event ClientUtils.EventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded; + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index ae70abbdf69b..9432c669a719 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -60,7 +60,7 @@ namespace YourProject { {{#authMethods}}// the type of token here depends on the api security specifications ApiKeyToken token = new(""); - options.UseTokens(token); + options.AddTokens(token); // optionally choose the method the tokens will be provided with, default is RateLimitProvider options.UseProvider, ApiKeyToken>(); @@ -70,10 +70,10 @@ namespace YourProject // your custom converters if any }); - options.ConfigureClients(client => {{#supportsRetry}}client + options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(builder: builder => builder .AddRetryPolicy(2) .AddTimeoutPolicy(TimeSpan.FromSeconds(5)) - .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)){{/supportsRetry}} + .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)) // add whatever middleware you prefer ); }); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index bc808ade53e4..9fbb438e3003 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -20,14 +20,8 @@ namespace {{packageName}}.{{apiPackage}} /// /// Represents a collection of functions to interact with the API endpoints /// - {{>visibility}} interface {{interfacePrefix}}{{classname}} + {{>visibility}} interface {{interfacePrefix}}{{classname}} : IApi { - /// - /// An event to track the health of the server. - /// If you store these event args, be sure to purge old event args to prevent a memory leak. - /// - event ClientUtils.EventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded; - {{#operation}} /// /// {{summary}} @@ -68,9 +62,9 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{^-last}} - {{/operation}} + {{/-last}}{{/operation}} } /// @@ -84,13 +78,40 @@ namespace {{packageName}}.{{apiPackage}} /// public event ClientUtils.EventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded; - private readonly ILogger<{{classname}}> _logger; - private readonly HttpClient _httpClient;{{#hasApiKeyMethods}} - private readonly TokenProvider _apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - private readonly TokenProvider _bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - private readonly TokenProvider _basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - private readonly TokenProvider _httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - private readonly TokenProvider _oauthTokenProvider;{{/hasOAuthMethods}} + /// + /// The logger + /// + public ILogger<{{classname}}> Logger { get; } + + /// + /// The HttpClient + /// + public HttpClient HttpClient { get; }{{#hasApiKeyMethods}} + + /// + /// A token provider of type + /// + public TokenProvider ApiKeyProvider { get; }{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + + /// + /// A token provider of type + /// + public TokenProvider BearerTokenProvider { get; }{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + + /// + /// A token provider of type + /// + public TokenProvider BasicTokenProvider { get; }{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + + /// + /// A token provider of type + /// + public TokenProvider HttpSignatureTokenProvider { get; }{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + + /// + /// A token provider of type + /// + public TokenProvider OauthTokenProvider { get; }{{/hasOAuthMethods}} /// /// Initializes a new instance of the class. @@ -103,13 +124,13 @@ namespace {{packageName}}.{{apiPackage}} TokenProvider httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}, TokenProvider oauthTokenProvider{{/hasOAuthMethods}}) { - _logger = logger; - _httpClient = httpClient;{{#hasApiKeyMethods}} - _apiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - _bearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - _basicTokenProvider = basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - _httpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - _oauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}} + Logger = logger; + HttpClient = httpClient;{{#hasApiKeyMethods}} + ApiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicTokenProvider = basicTokenProvider;{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}} } {{#operation}} @@ -179,7 +200,7 @@ namespace {{packageName}}.{{apiPackage}} {{/hasRequiredParams}}using HttpRequestMessage request = new HttpRequestMessage(); UriBuilder uriBuilder = new UriBuilder(); - uriBuilder.Host = ClientUtils.HOST; + uriBuilder.Host = HttpClient.BaseAddress!.Host; uriBuilder.Scheme = ClientUtils.SCHEME; uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} @@ -230,7 +251,7 @@ namespace {{packageName}}.{{apiPackage}} List tokens = new List();{{/-first}}{{#isApiKey}} - ApiKeyToken apiKey = (ApiKeyToken) await _apiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); + ApiKeyToken apiKey = (ApiKeyToken) await ApiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(apiKey);{{#isKeyInHeader}} @@ -243,25 +264,25 @@ namespace {{packageName}}.{{apiPackage}} {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} - BasicToken basicToken = (BasicToken) await _basicTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + BasicToken basicToken = (BasicToken) await BasicTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(basicToken); basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} - BearerToken bearerToken = (BearerToken) await _bearerTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + BearerToken bearerToken = (BearerToken) await BearerTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(bearerToken); bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} - OAuthToken oauthToken = (OAuthToken) await _oauthTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + OAuthToken oauthToken = (OAuthToken) await OauthTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(oauthToken); oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} - HttpSignatureToken signatureToken = (HttpSignatureToken) await _httpSignatureTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + HttpSignatureToken signatureToken = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(signatureToken); @@ -291,7 +312,7 @@ namespace {{packageName}}.{{apiPackage}} {{/-first}}{{/produces}} request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; - using HttpResponseMessage responseMessage = await _httpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + using HttpResponseMessage responseMessage = await HttpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); DateTime requestedAt = DateTime.UtcNow; @@ -305,7 +326,7 @@ namespace {{packageName}}.{{apiPackage}} } catch(Exception e) { - _logger.LogError(e, "An error occured while invoking ApiResponded."); + Logger.LogError(e, "An error occured while invoking ApiResponded."); } } @@ -321,7 +342,7 @@ namespace {{packageName}}.{{apiPackage}} } catch(Exception e) { - _logger.LogError(e, "An error occured while sending the request to the server."); + Logger.LogError(e, "An error occured while sending the request to the server."); throw; } } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache index 2dde04a9b637..4ee535e44058 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache @@ -1,23 +1,9 @@ {{>partial_header}} using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -{{#useRestSharp}} -using RestSharp; -{{/useRestSharp}} using Xunit; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; -using System.Threading.Tasks; - -using {{packageName}}.Client; -using {{packageName}}.{{apiPackage}}; -{{#hasImport}} -using {{packageName}}.{{modelPackage}}; -{{/hasImport}} +using {{packageName}}.{{apiPackage}};{{#hasImport}} +using {{packageName}}.{{modelPackage}};{{/hasImport}} namespace {{packageName}}.Test.Api { @@ -28,46 +14,15 @@ namespace {{packageName}}.Test.Api /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). /// Please update the test case below to test the API endpoint. /// - public sealed class {{classname}}Tests : IDisposable + public sealed class {{classname}}Tests : ApiTestsBase { private readonly {{interfacePrefix}}{{classname}} _instance; - public {{classname}}Tests() - { - var host = CreateHostBuilder(Array.Empty()).Build(); - _instance = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => - { - {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new(TimeSpan.FromSeconds(1), $""); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - BearerToken bearerToken = new(TimeSpan.FromSeconds(1), $""); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - BasicToken basicToken = new(TimeSpan.FromSeconds(1), "", ""); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new(todo); - HttpSignatureToken httpSignatureToken = new(TimeSpan.FromSeconds(1), config); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - OAuthToken oauthToken = new(TimeSpan.FromSeconds(1), "token"); - options.AddTokens(oauthToken);{{/hasOAuthMethods}} - }); - - public void Dispose() + public {{classname}}Tests(): base(Array.Empty()) { - // Cleanup when everything is done. + _instance = _host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); } - /// - /// Test an instance of {{classname}} - /// - [Fact] - public void {{operationId}}InstanceTest() - { - // TODO uncomment below to test 'IsType' {{classname}} - //Assert.IsType<{{classname}}>(_instance); - } {{#operations}} {{#operation}} @@ -75,7 +30,7 @@ namespace {{packageName}}.Test.Api /// Test {{operationId}} /// [Fact] - public async Task {{operationId}}AsyncTest() + public void {{operationId}}AsyncTest() { // TODO uncomment below to test the method and replace null with proper value {{#allParams}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache new file mode 100644 index 000000000000..b2c4fbe7bbff --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache @@ -0,0 +1,41 @@ +{{>partial_header}} +using System; +using Microsoft.Extensions.Hosting; +using {{packageName}}.Client;{{#hasImport}} +using {{packageName}}.{{modelPackage}};{{/hasImport}} + +namespace {{packageName}}.Test.Api +{ + /// + /// Base class for API tests + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public class ApiTestsBase + { + protected readonly IHost _host; + + public ApiTestsBase(string[] args) + { + _host = CreateHostBuilder(args).Build(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + { + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken);{{/hasOAuthMethods}} + }); + } +} From deba53665b58daaa79a7029582d0653ed851396d Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Mon, 20 Dec 2021 22:37:16 -0500 Subject: [PATCH 17/35] removed a hard coded project name --- .../csharp-netcore/libraries/generichost/ClientUtils.mustache | 2 +- .../libraries/generichost/HostConfiguration.mustache | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index b4095b42feca..71ed2f67a9e1 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -282,7 +282,7 @@ namespace {{packageName}}.Client private static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(IServiceCollection services, HostConfiguration host) { if (!host.HttpClientsAdded) - host.AddCmcHttpClients(); + host.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(); // ensure that a token provider was provided for this token type // if not, default to RateLimitProvider diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index 65117c936d91..aaed53fec679 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -61,7 +61,7 @@ namespace {{packageName}}.Client public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients( Action? client = null, Action? builder = null) { - AddCmcHttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); + Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); return this; } From b695cf7a02fcb9314e7ad1dc503c9cfe9713e993 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Tue, 21 Dec 2021 00:21:48 -0500 Subject: [PATCH 18/35] fixed nrt bugs --- .../generichost/HostConfiguration.mustache | 8 +++---- .../generichost/HttpSigningToken.mustache | 2 +- .../libraries/generichost/api.mustache | 22 +++++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index aaed53fec679..dfb345e51f25 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -35,7 +35,7 @@ namespace {{packageName}}.Client /// public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}T{{classname}}{{^-last}}, {{/-last}}{{/apis}}> ( - Action? client = null, Action? builder = null){{#apis}} + Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null){{#apis}} where T{{classname}} : class, {{interfacePrefix}}{{classname}}{{/apis}} { client ??= c => c.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS); @@ -59,7 +59,7 @@ namespace {{packageName}}.Client /// /// public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients( - Action? client = null, Action? builder = null) + Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null) { Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); @@ -86,7 +86,7 @@ namespace {{packageName}}.Client /// public HostConfiguration AddTokens(TTokenBase token) where TTokenBase : TokenBase { - return AddTokens(new TTokenBase[]{ token }); + return AddTokens(new TTokenBase[]{ token }); } /// @@ -98,7 +98,7 @@ namespace {{packageName}}.Client public HostConfiguration AddTokens(IEnumerable tokens) where TTokenBase : TokenBase { TokenContainer container = new TokenContainer(tokens); - _services.AddSingleton>(services => container); + _services.AddSingleton(services => container); return this; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache index 3b47eb30ed2b..ca47cca24da3 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HttpSigningToken.mustache @@ -32,7 +32,7 @@ namespace {{packageName}}.Client /// /// /// - public void UseInHeader(System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} cancellationToken) + public void UseInHeader(System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken? cancellationToken = null) { var signedHeaders = _configuration.GetHttpSignedHeader(request, requestBody, cancellationToken); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index 9fbb438e3003..a8a2db09b9bb 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -49,7 +49,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null); + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{#nullableReferenceTypes}} /// /// {{summary}} @@ -61,8 +61,8 @@ namespace {{packageName}}.{{apiPackage}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. - /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{^-last}} + /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}?> + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}?> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{/nullableReferenceTypes}}{{^-last}} {{/-last}}{{/operation}} } @@ -132,7 +132,7 @@ namespace {{packageName}}.{{apiPackage}} HttpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}} } - {{#operation}} + {{#operation}} /// /// {{summary}} {{notes}} @@ -146,8 +146,12 @@ namespace {{packageName}}.{{apiPackage}} public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); - return result.Content ?? throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); - } + + if (result.Content == null) + throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); + + return result.Content; + }{{#nullableReferenceTypes}} {{! without NRT we dont know the return type. Microsoft has enabled NRT since .NET 6, so we should encourage it to be enabled here. }} /// /// {{summary}} {{notes}} @@ -158,9 +162,9 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> - public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}?> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { - ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null; + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>? result = null; try { result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); @@ -172,7 +176,7 @@ namespace {{packageName}}.{{apiPackage}} return result != null && result.IsSuccessStatusCode ? result.Content : null; - } + }{{/nullableReferenceTypes}} /// /// {{summary}} {{notes}} From 0c1c3826d737fee4514c00fbcc69651a4f76120e Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Tue, 28 Dec 2021 18:39:27 -0500 Subject: [PATCH 19/35] improved NRT and .net 3.1 support --- .../languages/CSharpNetCoreClientCodegen.java | 5 +- .../generichost/ApiResponse`1.mustache | 3 +- ...st_base.mustache => ApiTestsBase.mustache} | 16 +++-- .../generichost/ClientUtils.mustache | 8 +-- .../DependencyInjectionTests.mustache | 60 ++++++++++++------- .../generichost/RateLimitProvider`1.mustache | 11 ++-- .../libraries/generichost/TokenBase.mustache | 4 +- .../generichost/TokenContainer`1.mustache | 3 +- .../libraries/generichost/api.mustache | 54 +++++++++++++---- 9 files changed, 110 insertions(+), 54 deletions(-) rename modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/{api_test_base.mustache => ApiTestsBase.mustache} (68%) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 6db5c4ddeef6..e07447f618ab 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -775,7 +775,7 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S if (Boolean.FALSE.equals(excludeTests.get())) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); - supportingFiles.add(new SupportingFile("api_test_base.mustache", testPackageFolder + File.separator + "Api", "ApiTestsBase.cs")); + supportingFiles.add(new SupportingFile("ApiTestsBase.mustache", testPackageFolder + File.separator + "Api", "ApiTestsBase.cs")); supportingFiles.add(new SupportingFile("DependencyInjectionTests.mustache", testPackageFolder + File.separator + "Api", "DependencyInjectionTests.cs")); } @@ -1135,6 +1135,9 @@ protected void configureAdditionalPropertiesForFrameworks(final Map Boolean.TRUE.equals(p.isNetStandard))); + for (FrameworkStrategy frameworkStrategy : frameworkStrategies) { + properties.put(frameworkStrategy.name, strategies.stream().anyMatch(s -> s.name.equals(frameworkStrategy.name))); + } } /** diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache index 3c618b0b07aa..cd3bddfe3f5e 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache @@ -41,7 +41,8 @@ namespace {{packageName}}.Client /// /// The deserialized content /// - public T{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} Content { get; set; } + {{! .net 3.1 does not support unconstrained T }} + public T{{#nullableReferenceTypes}}{{^netcoreapp3.1}}?{{/netcoreapp3.1}}{{/nullableReferenceTypes}} Content { get; set; } /// /// Gets or sets the status code (HTTP status code) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache similarity index 68% rename from modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache rename to modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache index b2c4fbe7bbff..5aa921d01b66 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test_base.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache @@ -1,5 +1,7 @@ {{>partial_header}} using System; +using System.Collections.Generic; +using System.Security.Cryptography; using Microsoft.Extensions.Hosting; using {{packageName}}.Client;{{#hasImport}} using {{packageName}}.{{modelPackage}};{{/hasImport}} @@ -26,14 +28,18 @@ namespace {{packageName}}.Test.Api .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + options.AddTokens(apiKeyToken); + {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + options.AddTokens(bearerToken); + {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + options.AddTokens(basicToken); + {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + options.AddTokens(httpSignatureToken); + {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} }); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 71ed2f67a9e1..cb66edb2bbf7 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -257,7 +257,7 @@ namespace {{packageName}}.Client { builder.ConfigureServices((context, services) => { - HostConfiguration config = new(services); + HostConfiguration config = new HostConfiguration(services); options(context, config); @@ -274,7 +274,7 @@ namespace {{packageName}}.Client /// public static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(this IServiceCollection services, Action options) { - HostConfiguration config = new(services); + HostConfiguration config = new HostConfiguration(services); options(config); Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(services, config); } @@ -287,13 +287,13 @@ namespace {{packageName}}.Client // ensure that a token provider was provided for this token type // if not, default to RateLimitProvider var containerServices = services.Where(s => s.ServiceType.IsGenericType && - s.ServiceType.GetGenericTypeDefinition().IsAssignableTo(typeof(TokenContainer<>))).ToArray(); + s.ServiceType.GetGenericTypeDefinition().IsAssignableFrom(typeof(TokenContainer<>))).ToArray(); foreach(var containerService in containerServices) { var tokenType = containerService.ServiceType.GenericTypeArguments[0]; - var provider = services.FirstOrDefault(s => s.ServiceType.IsAssignableTo(typeof(TokenProvider<>).MakeGenericType(tokenType))); + var provider = services.FirstOrDefault(s => s.ServiceType.IsAssignableFrom(typeof(TokenProvider<>).MakeGenericType(tokenType))); if (provider == null) { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache index 376a3899a3e3..70bab22cd433 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache @@ -1,10 +1,12 @@ {{>partial_header}} using System; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using System.Security.Cryptography; using {{packageName}}.Client; using {{packageName}}.{{apiPackage}}; using Xunit; -using Microsoft.Extensions.DependencyInjection; namespace {{packageName}}.Test.Api { @@ -17,14 +19,18 @@ namespace {{packageName}}.Test.Api Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + options.AddTokens(apiKeyToken); + {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + options.AddTokens(bearerToken); + {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + options.AddTokens(basicToken); + {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + options.AddTokens(httpSignatureToken); + {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} }) @@ -34,14 +40,18 @@ namespace {{packageName}}.Test.Api Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + options.AddTokens(apiKeyToken); + {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + options.AddTokens(bearerToken); + {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + options.AddTokens(basicToken); + {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + options.AddTokens(httpSignatureToken); + {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); @@ -54,14 +64,18 @@ namespace {{packageName}}.Test.Api services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + options.AddTokens(apiKeyToken); + {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + options.AddTokens(bearerToken); + {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + options.AddTokens(basicToken); + {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + options.AddTokens(httpSignatureToken); + {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} }); @@ -74,14 +88,18 @@ namespace {{packageName}}.Test.Api services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(apiKeyToken);{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + options.AddTokens(apiKeyToken); + {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(bearerToken);{{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} + options.AddTokens(bearerToken); + {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(basicToken);{{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} - HttpSignatureConfiguration config = new HttpSignatureConfiguration(todo); + options.AddTokens(basicToken); + {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} + HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); - options.AddTokens(httpSignatureToken);{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + options.AddTokens(httpSignatureToken); + {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache index 37d85bf6b406..4d2c3c99927e 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache @@ -3,8 +3,7 @@ {{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} using System; -using System.Linq; -using System.Collections.Generic; +using System.Threading.Channels; using {{packageName}}.Client; namespace {{packageName}} @@ -15,7 +14,7 @@ namespace {{packageName}} /// public class RateLimitProvider : TokenProvider where TTokenBase : TokenBase { - internal System.Threading.Channels.Channel AvailableTokens { get; } + internal Channel AvailableTokens { get; } /// /// Instantiates a ThrottledTokenProvider. Your tokens will be rate limited based on the token's timeout. @@ -26,12 +25,12 @@ namespace {{packageName}} foreach(TTokenBase token in _tokens) token.StartTimer(token.Timeout ?? TimeSpan.FromMilliseconds(40)); - System.Threading.Channels.BoundedChannelOptions options = new(_tokens.Length) + BoundedChannelOptions options = new BoundedChannelOptions(_tokens.Length) { - FullMode = System.Threading.Channels.BoundedChannelFullMode.DropWrite + FullMode = BoundedChannelFullMode.DropWrite }; - AvailableTokens = System.Threading.Channels.Channel.CreateBounded(options); + AvailableTokens = Channel.CreateBounded(options); for (int i = 0; i < _tokens.Length; i++) _tokens[i].TokenBecameAvailable += ((sender) => AvailableTokens.Writer.TryWrite((TTokenBase) sender)); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache index 7a2aa0ba3135..0b8db5d10ae1 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenBase.mustache @@ -12,8 +12,8 @@ namespace {{packageName}}.Client public abstract class TokenBase { private DateTime _nextAvailable = DateTime.UtcNow; - private object _nextAvailableLock = new(); - private readonly System.Timers.Timer _timer = new(); + private object _nextAvailableLock = new object(); + private readonly System.Timers.Timer _timer = new System.Timers.Timer(); internal TimeSpan? Timeout { get; set; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache index fb3018a91628..113f9206fd19 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/TokenContainer`1.mustache @@ -3,6 +3,7 @@ {{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} using System.Linq; +using System.Collections.Generic; namespace {{packageName}}.Client { @@ -15,7 +16,7 @@ namespace {{packageName}}.Client /// /// The collection of tokens /// - public System.Collections.Generic.List Tokens { get; } = new(); + public List Tokens { get; } = new List(); /// /// Instantiates a TokenContainer diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index a8a2db09b9bb..ccca4f5ba5af 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -49,7 +49,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{#nullableReferenceTypes}} + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{#nullableReferenceTypes}} /// /// {{summary}} @@ -62,7 +62,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// Task of ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}?> - Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}?> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{/nullableReferenceTypes}}{{^-last}} + Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{/nullableReferenceTypes}}{{^-last}} {{/-last}}{{/operation}} } @@ -142,16 +142,18 @@ namespace {{packageName}}.{{apiPackage}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} /// Cancellation Token to cancel the request. - /// <> - public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) + /// <> + public async Task<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); - if (result.Content == null) + {{^nullableReferenceTypes}}{{#returnTypeIsPrimitive}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' + {{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}}if (result.Content == null){{^nullableReferenceTypes}}{{#returnTypeIsPrimitive}} + #pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}} throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent); - + return result.Content; - }{{#nullableReferenceTypes}} {{! without NRT we dont know the return type. Microsoft has enabled NRT since .NET 6, so we should encourage it to be enabled here. }} + }{{#nullableReferenceTypes}} /// /// {{summary}} {{notes}} @@ -162,9 +164,9 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> - public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}?> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { - ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>? result = null; + ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null; try { result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); @@ -176,7 +178,33 @@ namespace {{packageName}}.{{apiPackage}} return result != null && result.IsSuccessStatusCode ? result.Content : null; - }{{/nullableReferenceTypes}} + }{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^returnTypeIsPrimitive}} +{{! Note that this method is a copy paste of above due to NRT complexities }} + /// + /// {{summary}} {{notes}} + /// + /// Thrown when fails to make API call + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// Cancellation Token to cancel the request. + /// <> + public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) + { + ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null; + try + { + result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false); + } + catch (Exception) + { + } + + return result != null && result.IsSuccessStatusCode + ? result.Content + : null; + } + {{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}} /// /// {{summary}} {{notes}} @@ -187,7 +215,7 @@ namespace {{packageName}}.{{apiPackage}} {{/allParams}} /// Cancellation Token to cancel the request. /// <> where T : - public async Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) + public async Task> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null) { try { @@ -290,7 +318,7 @@ namespace {{packageName}}.{{apiPackage}} tokens.Add(signatureToken); - string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync({{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}).ConfigureAwait(false); signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} @@ -320,7 +348,7 @@ namespace {{packageName}}.{{apiPackage}} DateTime requestedAt = DateTime.UtcNow; - string responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + string responseContent = await responseMessage.Content.ReadAsStringAsync({{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}).ConfigureAwait(false); if (ApiResponded != null) { From 63a395096bcc37b50bf71795aa1333c69e700504 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Tue, 28 Dec 2021 20:49:55 -0500 Subject: [PATCH 20/35] renamed projectName to apiName, added cli option --- .../codegen/CodegenConstants.java | 2 ++ .../languages/CSharpNetCoreClientCodegen.java | 32 +++++++++++++++---- .../generichost/ApiResponse`1.mustache | 2 +- .../generichost/ApiTestsBase.mustache | 2 +- .../generichost/ClientUtils.mustache | 12 +++---- .../DependencyInjectionTests.mustache | 12 +++---- .../generichost/HostConfiguration.mustache | 6 ++-- .../libraries/generichost/README.mustache | 8 ++--- 8 files changed, 48 insertions(+), 28 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index bb7aa069fe7a..a2fde5fe598a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -35,6 +35,8 @@ public class CodegenConstants { public static final String SKIP_FORM_MODEL = "skipFormModel"; /* /end System Properties */ + public static final String API_NAME = "apiName"; + public static final String API_PACKAGE = "apiPackage"; public static final String API_PACKAGE_DESC = "package for generated api classes"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index e07447f618ab..e37989350112 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -57,6 +57,8 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { // Project Variable, determined from target framework. Not intended to be user-settable. protected static final String TARGET_FRAMEWORK_VERSION = "targetFrameworkVersion"; + protected String apiName = "Api"; + @SuppressWarnings({"hiding"}) private final Logger LOGGER = LoggerFactory.getLogger(CSharpClientCodegen.class); private static final List frameworkStrategies = Arrays.asList( @@ -172,6 +174,10 @@ public CSharpNetCoreClientCodegen() { "C# package name (convention: Title.Case).", this.packageName); + addOption(CodegenConstants.API_NAME, + "Must be a valid C# class name. Only used in Generic Host library. Default: " + this.apiName, + this.apiName); + addOption(CodegenConstants.PACKAGE_VERSION, "C# package version.", this.packageVersion); @@ -269,8 +275,8 @@ public CSharpNetCoreClientCodegen() { this.optionalEmitDefaultValuesFlag); addSwitch(CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION, - CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION_DESC, - this.conditionalSerialization); + CodegenConstants.OPTIONAL_CONDITIONAL_SERIALIZATION_DESC, + this.conditionalSerialization); addSwitch(CodegenConstants.OPTIONAL_PROJECT_FILE, CodegenConstants.OPTIONAL_PROJECT_FILE_DESC, @@ -561,6 +567,12 @@ public void processOpts() { setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); } + if (additionalProperties.containsKey(CodegenConstants.API_NAME)) { + setApiName((String) additionalProperties.get(CodegenConstants.API_NAME)); + } else { + additionalProperties.put(CodegenConstants.API_NAME, apiName); + } + if (isEmpty(apiPackage)) { setApiPackage("Api"); } @@ -570,11 +582,6 @@ public void processOpts() { clientPackage = "Client"; - String projectName = (String) additionalProperties.getOrDefault("projectName", ""); - if (!"".equals(projectName) && (Boolean.FALSE.equals(projectName.matches("^[a-zA-Z0-9_]*$")) || Boolean.FALSE.equals(projectName.matches("^[a-zA-Z].*")))){ - throw new RuntimeException("Invalid project name " + projectName + ". May only contain alphanumeric characaters or underscore and start with a letter."); - } - if (GENERICHOST.equals(getLibrary())){ setLibrary(GENERICHOST); additionalProperties.put("useGenericHost", true); @@ -830,6 +837,17 @@ public void setPackageName(String packageName) { this.packageName = packageName; } + /** + * Sets the api name. This value must be a valid class name. + * @param apiName The api name + */ + public void setApiName(String apiName) { + if (!"".equals(apiName) && (Boolean.FALSE.equals(apiName.matches("^[a-zA-Z0-9_]*$")) || Boolean.FALSE.equals(apiName.matches("^[a-zA-Z].*")))){ + throw new RuntimeException("Invalid project name " + apiName + ". May only contain alphanumeric characaters or underscore and start with a letter."); + } + this.apiName = apiName; + } + @Override public void setPackageVersion(String packageVersion) { this.packageVersion = packageVersion; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache index cd3bddfe3f5e..ce8b308e9caf 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiResponse`1.mustache @@ -41,7 +41,7 @@ namespace {{packageName}}.Client /// /// The deserialized content /// - {{! .net 3.1 does not support unconstrained T }} + {{! .net 3.1 does not support unconstrained nullable T }} public T{{#nullableReferenceTypes}}{{^netcoreapp3.1}}?{{/netcoreapp3.1}}{{/nullableReferenceTypes}} Content { get; set; } /// diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache index 5aa921d01b66..0cc93d27c672 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache @@ -25,7 +25,7 @@ namespace {{packageName}}.Test.Api } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + .Configure{{apiName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index cb66edb2bbf7..66842dcdacb1 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -253,7 +253,7 @@ namespace {{packageName}}.Client /// /// /// - public static IHostBuilder Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(this IHostBuilder builder, Action options) + public static IHostBuilder Configure{{apiName}}(this IHostBuilder builder, Action options) { builder.ConfigureServices((context, services) => { @@ -261,7 +261,7 @@ namespace {{packageName}}.Client options(context, config); - Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(services, config); + Add{{apiName}}(services, config); }); return builder; @@ -272,17 +272,17 @@ namespace {{packageName}}.Client /// /// /// - public static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(this IServiceCollection services, Action options) + public static void Add{{apiName}}(this IServiceCollection services, Action options) { HostConfiguration config = new HostConfiguration(services); options(config); - Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(services, config); + Add{{apiName}}(services, config); } - private static void Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(IServiceCollection services, HostConfiguration host) + private static void Add{{apiName}}(IServiceCollection services, HostConfiguration host) { if (!host.HttpClientsAdded) - host.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(); + host.Add{{apiName}}HttpClients(); // ensure that a token provider was provided for this token type // if not, default to RateLimitProvider diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache index 70bab22cd433..ac4a4d8e2d11 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/DependencyInjectionTests.mustache @@ -16,7 +16,7 @@ namespace {{packageName}}.Test.Api public class DependencyInjectionTest { private readonly IHost _hostUsingConfigureWithoutAClient = - Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + Host.CreateDefaultBuilder(Array.Empty()).Configure{{apiName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); @@ -37,7 +37,7 @@ namespace {{packageName}}.Test.Api .Build(); private readonly IHost _hostUsingConfigureWithAClient = - Host.CreateDefaultBuilder(Array.Empty()).Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + Host.CreateDefaultBuilder(Array.Empty()).Configure{{apiName}}((context, options) => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); @@ -54,14 +54,14 @@ namespace {{packageName}}.Test.Api {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} - options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); + options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); }) .Build(); private readonly IHost _hostUsingAddWithoutAClient = Host.CreateDefaultBuilder(Array.Empty()).ConfigureServices((host, services) => { - services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => + services.Add{{apiName}}(options => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); @@ -85,7 +85,7 @@ namespace {{packageName}}.Test.Api private readonly IHost _hostUsingAddWithAClient = Host.CreateDefaultBuilder(Array.Empty()).ConfigureServices((host, services) => { - services.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}(options => + services.Add{{apiName}}(options => { {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); @@ -102,7 +102,7 @@ namespace {{packageName}}.Test.Api {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} - options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); + options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); }); }) .Build(); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index dfb345e51f25..cf70b60e18b3 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -33,7 +33,7 @@ namespace {{packageName}}.Client /// /// /// - public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}T{{classname}}{{^-last}}, {{/-last}}{{/apis}}> + public HostConfiguration Add{{apiName}}HttpClients<{{#apiInfo}}{{#apis}}T{{classname}}{{^-last}}, {{/-last}}{{/apis}}> ( Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null){{#apis}} where T{{classname}} : class, {{interfacePrefix}}{{classname}}{{/apis}} @@ -58,10 +58,10 @@ namespace {{packageName}}.Client /// /// /// - public HostConfiguration Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients( + public HostConfiguration Add{{apiName}}HttpClients( Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null) { - Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); + Add{{apiName}}HttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder); return this; } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index 9432c669a719..1a538db483ae 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -5,7 +5,7 @@ ```ps1 $properties = @( - 'projectName={{projectName}}' + 'apiName={{apiName}}' 'packageName={{packageName}}', 'targetFramework={{targetFramework}}' 'validatable={{validatable}}', @@ -56,7 +56,7 @@ namespace YourProject } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .Configure{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}((context, options) => + .Configure{{apiName}}((context, options) => { {{#authMethods}}// the type of token here depends on the api security specifications ApiKeyToken token = new(""); @@ -70,7 +70,7 @@ namespace YourProject // your custom converters if any }); - options.Add{{#projectName}}{{.}}{{/projectName}}{{^projectName}}Api{{/projectName}}HttpClients(builder: builder => builder + options.Add{{apiName}}HttpClients(builder: builder => builder .AddRetryPolicy(2) .AddTimeoutPolicy(TimeSpan.FromSeconds(5)) .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)) @@ -168,6 +168,7 @@ Class | Method | HTTP request | Description ## [OpenApi Generator Parameteres](https://openapi-generator.tech/docs/generators/csharp-netcore) - allowUnicodeIdentifiers: {{allowUnicodeIdentifiers}} +- apiName: {{apiName}} - caseInsensitiveResponseHeaders: {{caseInsensitiveResponseHeaders}} - conditionalSerialization: {{conditionalSerialization}} - disallowAdditionalPropertiesIfNotPresent: {{disallowAdditionalPropertiesIfNotPresent}} @@ -187,7 +188,6 @@ Class | Method | HTTP request | Description - packageName: {{packageName}} - packageTags: {{packageTags}} - packageVersion: {{packageVersion}} -- projectName: {{projectName}} - releaseNote: {{releaseNote}} - returnICollection: {{returnICollection}} - sortParamsByRequiredFlag: {{sortParamsByRequiredFlag}} From 6514b7774b5ac1ed95739fa2c78c3d8e13b9cb4c Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Tue, 28 Dec 2021 20:54:46 -0500 Subject: [PATCH 21/35] trying to avoid conflict --- .../codegen/languages/CSharpNetCoreClientCodegen.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index e37989350112..1455c176283f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -39,7 +39,9 @@ import static org.openapitools.codegen.utils.StringUtils.underscore; @SuppressWarnings("Duplicates") -public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { +public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { + protected String apiName = "Api"; + // Defines the sdk option for targeted frameworks, which differs from targetFramework and targetFrameworkNuget protected static final String MCS_NET_VERSION_KEY = "x-mcs-sdk"; protected static final String SUPPORTS_UWP = "supportsUWP"; @@ -57,8 +59,6 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { // Project Variable, determined from target framework. Not intended to be user-settable. protected static final String TARGET_FRAMEWORK_VERSION = "targetFrameworkVersion"; - protected String apiName = "Api"; - @SuppressWarnings({"hiding"}) private final Logger LOGGER = LoggerFactory.getLogger(CSharpClientCodegen.class); private static final List frameworkStrategies = Arrays.asList( From 4e908174563d279d39c8346610b3beed322bb8a7 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Thu, 30 Dec 2021 20:46:37 -0500 Subject: [PATCH 22/35] set GenerateAssemlbyInfo to true --- .../codegen/languages/CSharpNetCoreClientCodegen.java | 2 +- .../main/resources/csharp-netcore/netcore_project.mustache | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 1455c176283f..fd507ff22602 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -41,7 +41,7 @@ @SuppressWarnings("Duplicates") public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen { protected String apiName = "Api"; - + // Defines the sdk option for targeted frameworks, which differs from targetFramework and targetFrameworkNuget protected static final String MCS_NET_VERSION_KEY = "x-mcs-sdk"; protected static final String SUPPORTS_UWP = "supportsUWP"; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache index 4aa807c1a019..1a544ad8bb1e 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/netcore_project.mustache @@ -1,7 +1,8 @@ - - false + {{#useGenericHost}} + true {{/useGenericHost}}{{^useGenericHost}} + false{{/useGenericHost}} {{targetFramework}} {{packageName}} {{packageName}} From 190a3aa43b383a9ceb9c2dd1b9ff503503aae200 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Thu, 30 Dec 2021 20:51:30 -0500 Subject: [PATCH 23/35] created docs/scripts folder --- .../codegen/languages/CSharpNetCoreClientCodegen.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index fd507ff22602..84cb186c91b5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -770,7 +770,8 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("ClientUtils.mustache", clientPackageDir, "ClientUtils.cs")); supportingFiles.add(new SupportingFile("HostConfiguration.mustache", clientPackageDir, "HostConfiguration.cs")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "docs" + File.separator + "scripts", "git_push.sh")); + // TODO: supportingFiles.add(new SupportingFile("git_push.ps1.mustache", "docs" + File.separator + "scripts", "git_push.ps1")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); supportingFiles.add(new SupportingFile("netcore_project.mustache", packageFolder, packageName + ".csproj")); From fb3f3f50cc9dfe5e1456b39134b3e31f85054818 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Thu, 30 Dec 2021 21:15:37 -0500 Subject: [PATCH 24/35] moved ApiTestsBase.cs to not get overwritten --- .../codegen/languages/CSharpNetCoreClientCodegen.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 84cb186c91b5..ad1e8721e9e6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -691,6 +691,9 @@ public void processOpts() { if (Boolean.FALSE.equals(excludeTests.get())) { modelTestTemplateFiles.put("model_test.mustache", ".cs"); apiTestTemplateFiles.put("api_test.mustache", ".cs"); + if(GENERICHOST.equals(getLibrary())) { + apiTestTemplateFiles.put("ApiTestsBase.mustache", ".cs"); + } } if(HTTPCLIENT.equals(getLibrary())) { @@ -779,11 +782,10 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("AbstractOpenAPISchema.mustache", modelPackageDir, "AbstractOpenAPISchema.cs")); supportingFiles.add(new SupportingFile("OpenAPIDateConverter.mustache", clientPackageDir, "OpenAPIDateConverter.cs")); supportingFiles.add(new SupportingFile("ApiResponseEventArgs.mustache", clientPackageDir, "ApiResponseEventArgs.cs")); - supportingFiles.add(new SupportingFile("IApi.mustache", clientPackageDir, getInterfacePrefix() + "Api.cs")); + supportingFiles.add(new SupportingFile("IApi.mustache", clientPackageDir, getInterfacePrefix() + "Api.cs")); if (Boolean.FALSE.equals(excludeTests.get())) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); - supportingFiles.add(new SupportingFile("ApiTestsBase.mustache", testPackageFolder + File.separator + "Api", "ApiTestsBase.cs")); supportingFiles.add(new SupportingFile("DependencyInjectionTests.mustache", testPackageFolder + File.separator + "Api", "DependencyInjectionTests.cs")); } From a3a9a3084788ca227a1d27614ca1cfaa38dbb12f Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Fri, 31 Dec 2021 01:49:32 -0500 Subject: [PATCH 25/35] test fixes and improvements --- .../languages/CSharpNetCoreClientCodegen.java | 37 +++++++++++++++++-- .../generichost/ApiTestsBase.mustache | 16 ++++---- .../libraries/generichost/api_test.mustache | 20 +++++----- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index ad1e8721e9e6..546b10331db6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -691,9 +691,6 @@ public void processOpts() { if (Boolean.FALSE.equals(excludeTests.get())) { modelTestTemplateFiles.put("model_test.mustache", ".cs"); apiTestTemplateFiles.put("api_test.mustache", ".cs"); - if(GENERICHOST.equals(getLibrary())) { - apiTestTemplateFiles.put("ApiTestsBase.mustache", ".cs"); - } } if(HTTPCLIENT.equals(getLibrary())) { @@ -710,6 +707,30 @@ else if (GENERICHOST.equals(getLibrary())){ additionalProperties.put("apiDocPath", apiDocPath); additionalProperties.put("modelDocPath", modelDocPath); + addTestInstructions(); + } + + private void addTestInstructions(){ + if (GENERICHOST.equals(getLibrary())){ + additionalProperties.put("testInstructions", + "/* *********************************************************************************" + + "\n* Follow these manual steps to construct tests." + + "\n* This file will not be overwritten." + + "\n* *********************************************************************************" + + "\n* 1. Navigate to ApiTests.Base.cs and ensure any tokens are being created correctly." + + "\n* Take care not to commit credentials to any repository." + + "\n*" + + "\n# 2. If you want to mock the server's response, use method AddApiHttpClients" + + "\n and provide your mock classes." + + "\n" + + "\n* 3. Locate the test you want below" + + "\n* - remove the skip property from the Fact attribute" + + "\n* - set the value of any variables if necessary" + + "\n*" + + "\n* 4. Run the tests and ensure they work." + + "\n*" + + "\n*/"); + } } public void addRestSharpSupportingFiles(final String clientPackageDir, final String packageFolder, @@ -784,9 +805,17 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("ApiResponseEventArgs.mustache", clientPackageDir, "ApiResponseEventArgs.cs")); supportingFiles.add(new SupportingFile("IApi.mustache", clientPackageDir, getInterfacePrefix() + "Api.cs")); + String apiTestFolder = testFolder + File.separator + testPackageName() + File.separator + apiPackage(); + if (Boolean.FALSE.equals(excludeTests.get())) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); - supportingFiles.add(new SupportingFile("DependencyInjectionTests.mustache", testPackageFolder + File.separator + "Api", "DependencyInjectionTests.cs")); + supportingFiles.add(new SupportingFile("DependencyInjectionTests.mustache", apiTestFolder, "DependencyInjectionTests.cs")); + + // do not overwrite test file that already exists + File apiTestsBaseFile = new File(apiTestFileFolder() + File.separator + "ApiTestsBase.cs"); + if (!apiTestsBaseFile.exists()) { + supportingFiles.add(new SupportingFile("ApiTestsBase.mustache", apiTestFolder, "ApiTestsBase.cs")); + } } if (ProcessUtils.hasHttpSignatureMethods(openAPI)) { diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache index 0cc93d27c672..57dd3c7edee2 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ApiTestsBase.mustache @@ -6,15 +6,15 @@ using Microsoft.Extensions.Hosting; using {{packageName}}.Client;{{#hasImport}} using {{packageName}}.{{modelPackage}};{{/hasImport}} + +{{{testInstructions}}} + + namespace {{packageName}}.Test.Api { /// /// Base class for API tests /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// public class ApiTestsBase { protected readonly IHost _host; @@ -27,20 +27,20 @@ namespace {{packageName}}.Test.Api public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .Configure{{apiName}}((context, options) => { - {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken($"", timeout: TimeSpan.FromSeconds(1)); + {{#hasApiKeyMethods}}ApiKeyToken apiKeyToken = new ApiKeyToken(context.Configuration[""], timeout: TimeSpan.FromSeconds(1)); options.AddTokens(apiKeyToken); {{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} - BearerToken bearerToken = new BearerToken($"", timeout: TimeSpan.FromSeconds(1)); + BearerToken bearerToken = new BearerToken(context.Configuration[""], timeout: TimeSpan.FromSeconds(1)); options.AddTokens(bearerToken); {{/hasHttpBearerMethods}}{{#hasHttpBasicMethods}} - BasicToken basicToken = new BasicToken("", "", timeout: TimeSpan.FromSeconds(1)); + BasicToken basicToken = new BasicToken(context.Configuration[""], context.Configuration[""], timeout: TimeSpan.FromSeconds(1)); options.AddTokens(basicToken); {{/hasHttpBasicMethods}}{{#hasHttpSignatureMethods}} HttpSigningConfiguration config = new HttpSigningConfiguration("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); HttpSignatureToken httpSignatureToken = new HttpSignatureToken(config, timeout: TimeSpan.FromSeconds(1)); options.AddTokens(httpSignatureToken); {{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} - OAuthToken oauthToken = new OAuthToken("token", timeout: TimeSpan.FromSeconds(1)); + OAuthToken oauthToken = new OAuthToken(context.Configuration[""], timeout: TimeSpan.FromSeconds(1)); options.AddTokens(oauthToken);{{/hasOAuthMethods}} }); } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache index 4ee535e44058..265bf40550c9 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache @@ -1,19 +1,20 @@ {{>partial_header}} using System; +using System.Threading.Tasks; using Xunit; using Microsoft.Extensions.DependencyInjection; using {{packageName}}.{{apiPackage}};{{#hasImport}} using {{packageName}}.{{modelPackage}};{{/hasImport}} + +{{{testInstructions}}} + + namespace {{packageName}}.Test.Api { /// /// Class for testing {{classname}} /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// public sealed class {{classname}}Tests : ApiTestsBase { private readonly {{interfacePrefix}}{{classname}} _instance; @@ -29,16 +30,15 @@ namespace {{packageName}}.Test.Api /// /// Test {{operationId}} /// - [Fact] - public void {{operationId}}AsyncTest() + [Fact (Skip = "not implemented")] + public async Task {{operationId}}AsyncTest() { - // TODO uncomment below to test the method and replace null with proper value {{#allParams}} - //{{{dataType}}} {{paramName}} = null; + {{{dataType}}} {{paramName}} = default; {{/allParams}} - //{{#returnType}}var response = await {{/returnType}}_instance.{{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + {{#returnType}}var response = await {{/returnType}}_instance.{{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); {{#returnType}} - //Assert.IsType<{{{.}}}>(response); + Assert.IsType<{{{.}}}>(response); {{/returnType}} } {{/operation}} From 41333346f1ddcc281d5cbc44ab86112ac83a46bc Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Fri, 31 Dec 2021 15:26:11 -0500 Subject: [PATCH 26/35] fixed licenseId bug, updated readme --- .../languages/CSharpNetCoreClientCodegen.java | 14 ++++-- .../libraries/generichost/README.mustache | 50 ++++++++++++------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index 546b10331db6..ec5abc176d33 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -567,6 +567,10 @@ public void processOpts() { setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); } + if (additionalProperties.containsKey((CodegenConstants.LICENSE_ID))) { + setLicenseId((String) additionalProperties.get(CodegenConstants.LICENSE_ID)); + } + if (additionalProperties.containsKey(CodegenConstants.API_NAME)) { setApiName((String) additionalProperties.get(CodegenConstants.API_NAME)); } else { @@ -720,9 +724,10 @@ private void addTestInstructions(){ "\n* 1. Navigate to ApiTests.Base.cs and ensure any tokens are being created correctly." + "\n* Take care not to commit credentials to any repository." + "\n*" + - "\n# 2. If you want to mock the server's response, use method AddApiHttpClients" + - "\n and provide your mock classes." + - "\n" + + "\n* 2. Mocking is coordinated by ApiTestsBase#AddApiHttpClients." + + "\n* To mock the client, use the generic AddApiHttpClients." + + "\n* To mock the server, change the client's BaseAddress." + + "\n*" + "\n* 3. Locate the test you want below" + "\n* - remove the skip property from the Fact attribute" + "\n* - set the value of any variables if necessary" + @@ -796,6 +801,7 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "docs" + File.separator + "scripts", "git_push.sh")); // TODO: supportingFiles.add(new SupportingFile("git_push.ps1.mustache", "docs" + File.separator + "scripts", "git_push.ps1")); + // TODO: supportingFiles.add(new SupportingFile("generate.ps1.mustache", "docs" + File.separator + "scripts", "generate.ps1")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); supportingFiles.add(new SupportingFile("netcore_project.mustache", packageFolder, packageName + ".csproj")); @@ -864,6 +870,7 @@ public void setPackageGuid(String packageGuid) { this.packageGuid = packageGuid; } + // TODO: this does the same as super @Override public void setPackageName(String packageName) { this.packageName = packageName; @@ -880,6 +887,7 @@ public void setApiName(String apiName) { this.apiName = apiName; } + // TODO: this does the same as super @Override public void setPackageVersion(String packageVersion) { this.packageVersion = packageVersion; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache index 1a538db483ae..608e3fa8654c 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/README.mustache @@ -5,29 +5,41 @@ ```ps1 $properties = @( - 'apiName={{apiName}}' - 'packageName={{packageName}}', - 'targetFramework={{targetFramework}}' + 'apiName={{apiName}}', + 'targetFramework={{targetFramework}}', 'validatable={{validatable}}', 'nullableReferenceTypes={{nullableReferenceTypes}}', 'hideGenerationTimestamp={{hideGenerationTimestamp}}', - 'packageVersion={{packageVersion}}' + 'packageVersion={{packageVersion}}', + 'packageAuthors={{packageAuthors}}', + 'packageCompany={{packageCompany}}', + 'packageCopyright={{packageCopyright}}', + 'packageDescription={{packageDescription}}',{{#licenseId}} + 'licenseId={{.}}',{{/licenseId}} + 'packageName={{packageName}}', + 'packageTags={{packageTags}}', + 'packageTitle={{packageTitle}}' ) -join "," $global = @( - 'apiDocs={{generateApiDocs}}' + 'apiDocs={{generateApiDocs}}', 'modelDocs={{generateModelDocs}}', 'apiTests={{generateApiTests}}', - 'modelTests={{generateModelTests}}', + 'modelTests={{generateModelTests}}' ) -join "," java -jar "/openapi-generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar" generate ` -g csharp-netcore ` - --library generichost ` - -i .yml ` + -i .yaml ` -o ` + --library generichost ` --additional-properties $properties ` - --global-property $global + --global-property $global ` + --git-host "{{gitHost}}" ` + --git-repo-id "{{gitRepoId}}" ` + --git-user-id "{{gitUserId}}" ` + --release-note "{{releaseNote}}" + # -t templates ``` @@ -49,9 +61,7 @@ namespace YourProject 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}} - ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/-first}}{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} } @@ -93,12 +103,6 @@ namespace YourProject 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. - -## Notes - -- If you are not using .Net Standard, consider setting validatable to false in your generator-config.json due to this bug: https://github.com/dotnet/project-system/issues/3934 -- If you want to override the provided mustache templates, provide generate the library with the -t parameter and the folder name which stores your templates. - ## Dependencies @@ -150,7 +154,7 @@ Class | Method | HTTP request | Description - Build date: {{generatedDate}}{{/hideGenerationTimestamp}} - Build package: {{generatorClass}} -## Api Configuration +## Api Information - appName: {{appName}} - appVersion: {{appVersion}} - appDescription: {{appDescription}} @@ -172,6 +176,9 @@ Class | Method | HTTP request | Description - caseInsensitiveResponseHeaders: {{caseInsensitiveResponseHeaders}} - conditionalSerialization: {{conditionalSerialization}} - disallowAdditionalPropertiesIfNotPresent: {{disallowAdditionalPropertiesIfNotPresent}} +- gitHost: {{gitHost}} +- gitRepoId: {{gitRepoId}} +- gitUserId: {{gitUserId}} - hideGenerationTimestamp: {{hideGenerationTimestamp}} - interfacePrefix: {{interfacePrefix}} - library: {{library}} @@ -184,9 +191,14 @@ Class | Method | HTTP request | Description - optionalEmitDefaultValues: {{optionalEmitDefaultValues}} - optionalMethodArgument: {{optionalMethodArgument}} - optionalProjectFile: {{optionalProjectFile}} +- packageAuthors: {{packageAuthors}} +- packageCompany: {{packageCompany}} +- packageCopyright: {{packageCopyright}} +- packageDescription: {{packageDescription}} - packageGuid: {{packageGuid}} - packageName: {{packageName}} - packageTags: {{packageTags}} +- packageTitle: {{packageTitle}} - packageVersion: {{packageVersion}} - releaseNote: {{releaseNote}} - returnICollection: {{returnICollection}} @@ -197,6 +209,6 @@ Class | Method | HTTP request | Description - useDateTimeOffset: {{useDateTimeOffset}} - useOneOfDiscriminatorLookup: {{useOneOfDiscriminatorLookup}} - validatable: {{validatable}}{{#infoUrl}} -For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}){{/infoUrl}} +For more information, please visit [{{{.}}}]({{{.}}}){{/infoUrl}} This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project. \ No newline at end of file From c87c2027188f5b33146ad2b1e63061e818732706 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sun, 2 Jan 2022 21:35:49 -0500 Subject: [PATCH 27/35] build samples --- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../OpenAPIClient/src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- .../src/Org.OpenAPITools/Org.OpenAPITools.csproj | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/client/others/csharp-netcore-complex-files/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/others/csharp-netcore-complex-files/src/Org.OpenAPITools/Org.OpenAPITools.csproj index c445b1abb7cb..ea3a254a4d22 100644 --- a/samples/client/others/csharp-netcore-complex-files/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/others/csharp-netcore-complex-files/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netstandard2.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClient-ConditionalSerialization/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClient-ConditionalSerialization/src/Org.OpenAPITools/Org.OpenAPITools.csproj index c445b1abb7cb..ea3a254a4d22 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClient-ConditionalSerialization/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClient-ConditionalSerialization/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netstandard2.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClient-httpclient/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClient-httpclient/src/Org.OpenAPITools/Org.OpenAPITools.csproj index 0b425a6b53a1..2d4de6e7cccf 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClient-httpclient/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClient-httpclient/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netstandard2.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClient-net47/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClient-net47/src/Org.OpenAPITools/Org.OpenAPITools.csproj index 74dc32216c87..5e0f256b4cc5 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClient-net47/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClient-net47/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false net47 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClient-net5.0/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClient-net5.0/src/Org.OpenAPITools/Org.OpenAPITools.csproj index d2fb8de69812..40cff9b3c47c 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClient-net5.0/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClient-net5.0/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false net5.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClient/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClient/src/Org.OpenAPITools/Org.OpenAPITools.csproj index c445b1abb7cb..ea3a254a4d22 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClient/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClient/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netstandard2.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClientCore/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClientCore/src/Org.OpenAPITools/Org.OpenAPITools.csproj index 19789c359524..97a88c1cbe33 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClientCore/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClientCore/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netcoreapp2.0 Org.OpenAPITools Org.OpenAPITools diff --git a/samples/client/petstore/csharp-netcore/OpenAPIClientCoreAndNet47/src/Org.OpenAPITools/Org.OpenAPITools.csproj b/samples/client/petstore/csharp-netcore/OpenAPIClientCoreAndNet47/src/Org.OpenAPITools/Org.OpenAPITools.csproj index eaf3365f5f6d..6310b388e6e9 100644 --- a/samples/client/petstore/csharp-netcore/OpenAPIClientCoreAndNet47/src/Org.OpenAPITools/Org.OpenAPITools.csproj +++ b/samples/client/petstore/csharp-netcore/OpenAPIClientCoreAndNet47/src/Org.OpenAPITools/Org.OpenAPITools.csproj @@ -1,7 +1,7 @@ - false + false netstandard2.1;netcoreapp3.0 Org.OpenAPITools Org.OpenAPITools From 62c5b991be4fe26dc0264db8b23dc4cb11eb7c32 Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sun, 2 Jan 2022 21:38:46 -0500 Subject: [PATCH 28/35] export docs --- docs/generators/csharp-netcore.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generators/csharp-netcore.md b/docs/generators/csharp-netcore.md index 42b52614ba4c..9da48381b6cd 100644 --- a/docs/generators/csharp-netcore.md +++ b/docs/generators/csharp-netcore.md @@ -8,6 +8,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl | Option | Description | Values | Default | | ------ | ----------- | ------ | ------- | |allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false| +|apiName|Must be a valid C# class name. Only used in Generic Host library. Default: Api| |Api| |caseInsensitiveResponseHeaders|Make API response's headers case-insensitive| |false| |conditionalSerialization|Serialize only those properties which are initialized by user, accepted values are true or false, default value is false.| |false| |disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| From b106cf45ecd754e205176d4a55744f369454a26f Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Wed, 5 Jan 2022 21:11:04 -0500 Subject: [PATCH 29/35] removed new language features --- .../generichost/HostConfiguration.mustache | 4 +- .../libraries/generichost/api.mustache | 240 +++++++++--------- 2 files changed, 124 insertions(+), 120 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache index cf70b60e18b3..24530537f967 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/HostConfiguration.mustache @@ -38,7 +38,9 @@ namespace {{packageName}}.Client Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null){{#apis}} where T{{classname}} : class, {{interfacePrefix}}{{classname}}{{/apis}} { - client ??= c => c.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS); + if (client == null) + client = c => c.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS); + List builders = new List(); {{#apis}}builders.Add(_services.AddHttpClient<{{interfacePrefix}}{{classname}}, T{{classname}}>(client)); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index ccca4f5ba5af..afd37d6d7987 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -229,149 +229,151 @@ namespace {{packageName}}.{{apiPackage}} #pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - {{/hasRequiredParams}}using HttpRequestMessage request = new HttpRequestMessage(); - - UriBuilder uriBuilder = new UriBuilder(); - uriBuilder.Host = HttpClient.BaseAddress!.Host; - uriBuilder.Scheme = ClientUtils.SCHEME; - uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} - uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} - - if ({{paramName}} != null) - uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; - {{/required}}{{/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}} + {{/hasRequiredParams}}using (HttpRequestMessage request = new HttpRequestMessage()) + { + UriBuilder uriBuilder = new UriBuilder(); + uriBuilder.Host = HttpClient.BaseAddress{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.Host; + uriBuilder.Scheme = ClientUtils.SCHEME; + uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}} + uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}} -{{! all the redundant tags here are to get the spacing just right }} - {{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); - {{/required}}{{/queryParams}}{{#queryParams}}{{#-first}} - {{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null) - parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + if ({{paramName}} != null) + uriBuilder.Path = uriBuilder.Path + $"/{ Uri.EscapeDataString({{paramName}}).ToString()) }"; + {{/required}}{{/pathParams}}{{#queryParams}}{{#-first}} - {{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}} - - request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} - - if ({{paramName}} != null) - request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} + 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}} - MultipartContent multipartContent = new MultipartContent(); + {{! all the redundant tags here are to get the spacing just right }} + {{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); + {{/required}}{{/queryParams}}{{#queryParams}}{{#-first}} + {{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null) + parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}); - request.Content = multipartContent;{{#formParams}}{{#-first}} - - List> formParams = new List>(); + {{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}} + + request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} - multipartContent.Add(new FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}} - - formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}} + MultipartContent multipartContent = new MultipartContent(); - if ({{paramName}} != null) - formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}} - - multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{^required}} - - if ({{paramName}} != null) - multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} + request.Content = multipartContent;{{#formParams}}{{#-first}} - if (({{paramName}} as object) is System.IO.Stream stream) - request.Content = new StreamContent(stream); - else - request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} + List> formParams = new List>(); - List tokens = new List();{{/-first}}{{#isApiKey}} + multipartContent.Add(new FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}} - ApiKeyToken apiKey = (ApiKeyToken) await ApiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); - - tokens.Add(apiKey);{{#isKeyInHeader}} - - apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} - - apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} - - apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} - -{{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} - request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} - - BasicToken basicToken = (BasicToken) await BasicTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}} - tokens.Add(basicToken); - - basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} - - BearerToken bearerToken = (BearerToken) await BearerTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + if ({{paramName}} != null) + formParams.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}} + + multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{^required}} + + if ({{paramName}} != null) + multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} + + if (({{paramName}} as object) is System.IO.Stream stream) + request.Content = new StreamContent(stream); + else + request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} + + List tokens = new List();{{/-first}}{{#isApiKey}} + + ApiKeyToken apiKey = (ApiKeyToken) await ApiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); + + tokens.Add(apiKey);{{#isKeyInHeader}} + + apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} + + apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} + + apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} + + {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} + request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} - tokens.Add(bearerToken); - - bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} + BasicToken basicToken = (BasicToken) await BasicTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); - OAuthToken oauthToken = (OAuthToken) await OauthTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + tokens.Add(basicToken); + + basicToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBasic}}{{#isBasicBearer}} - tokens.Add(oauthToken); - - oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} - - HttpSignatureToken signatureToken = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); + BearerToken bearerToken = (BearerToken) await BearerTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); - tokens.Add(signatureToken); + tokens.Add(bearerToken); + + bearerToken.UseInHeader(request, "{{keyParamName}}");{{/isBasicBearer}}{{#isOAuth}} - string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync({{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}).ConfigureAwait(false); + OAuthToken oauthToken = (OAuthToken) await OauthTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); - signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} - - string[] contentTypes = new string[] { - {{/-first}}"{{{mediaType}}}"{{^-last}}, - {{/-last}}{{#-last}} - };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} - - string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = ClientUtils.SelectHeaderContentType(contentTypes); + tokens.Add(oauthToken); + + oauthToken.UseInHeader(request, "{{keyParamName}}");{{/isOAuth}}{{#isHttpSignature}} + + HttpSignatureToken signatureToken = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellationToken).ConfigureAwait(false); - if (contentType != null) - request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}} - - string[] accepts = new string[] { {{/-first}}{{/produces}} - {{#produces}}"{{{mediaType}}}"{{^-last}}, - {{/-last}}{{/produces}}{{#produces}}{{#-last}} - };{{/-last}}{{/produces}}{{#produces}}{{#-first}} - - string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = ClientUtils.SelectHeaderAccept(accepts); + tokens.Add(signatureToken); - if (accept != null) - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); - {{/-first}}{{/produces}} - request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; - - using HttpResponseMessage responseMessage = await HttpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false); + string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync({{^netStandard}}{{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}{{/netStandard}}).ConfigureAwait(false); - DateTime requestedAt = DateTime.UtcNow; + signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} + + string[] contentTypes = new string[] { + {{/-first}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{#-last}} + };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = ClientUtils.SelectHeaderContentType(contentTypes); - string responseContent = await responseMessage.Content.ReadAsStringAsync({{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}).ConfigureAwait(false); + if (contentType != null) + request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}} + + string[] accepts = new string[] { {{/-first}}{{/produces}} + {{#produces}}"{{{mediaType}}}"{{^-last}}, + {{/-last}}{{/produces}}{{#produces}}{{#-last}} + };{{/-last}}{{/produces}}{{#produces}}{{#-first}} + + string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = ClientUtils.SelectHeaderAccept(accepts); - if (ApiResponded != null) - { - try - { - ApiResponded.Invoke(this, new ApiResponseEventArgs(requestedAt, DateTime.UtcNow, responseMessage.StatusCode, "{{path}}")); - } - catch(Exception e) + if (accept != null) + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + {{/-first}}{{/produces}} + request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; + + using (HttpResponseMessage responseMessage = await HttpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false)) { - Logger.LogError(e, "An error occured while invoking ApiResponded."); + DateTime requestedAt = DateTime.UtcNow; + + string responseContent = await responseMessage.Content.ReadAsStringAsync({{^netStandard}}{{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}{{/netStandard}}).ConfigureAwait(false); + + if (ApiResponded != null) + { + try + { + ApiResponded.Invoke(this, new ApiResponseEventArgs(requestedAt, DateTime.UtcNow, responseMessage.StatusCode, "{{path}}")); + } + catch(Exception e) + { + Logger.LogError(e, "An error occured while invoking ApiResponded."); + } + } + + ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); + + if (apiResponse.IsSuccessStatusCode) + apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, ClientUtils.JsonSerializerSettings);{{#authMethods}} + else if (apiResponse.StatusCode == (HttpStatusCode) 429) + foreach(TokenBase token in tokens) + token.BeginRateLimit();{{/authMethods}} + + return apiResponse; } } - - ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent); - - if (apiResponse.IsSuccessStatusCode) - apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, ClientUtils.JsonSerializerSettings);{{#authMethods}} - else if (apiResponse.StatusCode == HttpStatusCode.TooManyRequests) - foreach(TokenBase token in tokens) - token.BeginRateLimit();{{/authMethods}} - - return apiResponse; - } + } catch(Exception e) { Logger.LogError(e, "An error occured while sending the request to the server."); From b45f066819a3f6dd2a3729f8a3be3d91f69c516e Mon Sep 17 00:00:00 2001 From: devhl-labs Date: Sat, 8 Jan 2022 17:52:28 -0500 Subject: [PATCH 30/35] added support for .net standard 2.0 --- .../generichost/ClientUtils.mustache | 2 +- .../generichost/RateLimitProvider`1.mustache | 74 +++++++++++++++++-- .../libraries/generichost/api.mustache | 7 +- .../libraries/generichost/api_test.mustache | 7 +- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache index 66842dcdacb1..b3b398425e43 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/ClientUtils.mustache @@ -299,7 +299,7 @@ namespace {{packageName}}.Client { services.AddSingleton(typeof(RateLimitProvider<>).MakeGenericType(tokenType)); services.AddSingleton(typeof(TokenProvider<>).MakeGenericType(tokenType), - services => services.GetRequiredService(typeof(RateLimitProvider<>).MakeGenericType(tokenType))); + s => s.GetRequiredService(typeof(RateLimitProvider<>).MakeGenericType(tokenType))); } } }{{#supportsRetry}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache index 4d2c3c99927e..3a778c0aeec9 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/RateLimitProvider`1.mustache @@ -2,11 +2,14 @@ {{>partial_header}} {{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}} -using System; -using System.Threading.Channels; -using {{packageName}}.Client; +using System;{{^netStandard}} +using System.Threading.Channels;{{/netStandard}}{{#netStandard}} +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks;{{/netStandard}} -namespace {{packageName}} +namespace {{packageName}}.Client {{^netStandard}} { /// /// Provides a token to the api clients. Tokens will be rate limited based on the provided TimeSpan. @@ -39,4 +42,65 @@ namespace {{packageName}} internal override async System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null) => await AvailableTokens.Reader.ReadAsync(cancellation.GetValueOrDefault()).ConfigureAwait(false); } -} +} {{/netStandard}}{{#netStandard}} +{ + /// + /// Provides a token to the api clients. Tokens will be rate limited based on the provided TimeSpan. + /// + /// + public class RateLimitProvider : TokenProvider where TTokenBase : TokenBase + { + internal ConcurrentDictionary AvailableTokens = new ConcurrentDictionary(); + private SemaphoreSlim _semaphore; + + /// + /// Instantiates a ThrottledTokenProvider. Your tokens will be rate limited based on the token's timeout. + /// + /// + public RateLimitProvider(TokenContainer container) : base(container.Tokens) + { + _semaphore = new SemaphoreSlim(1, 1); + + foreach(TTokenBase token in _tokens) + token.StartTimer(token.Timeout ?? TimeSpan.FromMilliseconds(40)); + + for (int i = 0; i < _tokens.Length; i++) + { + _tokens[i].TokenBecameAvailable += ((sender) => + { + TTokenBase token = (TTokenBase)sender; + + AvailableTokens.TryAdd(token, token); + }); + } + } + + internal override async System.Threading.Tasks.ValueTask GetAsync(System.Threading.CancellationToken? cancellation = null) + { + await _semaphore.WaitAsync().ConfigureAwait(false); + + try + { + TTokenBase result = null; + + while (result == null) + { + TTokenBase tokenToRemove = AvailableTokens.FirstOrDefault().Value; + + if (tokenToRemove != null && AvailableTokens.TryRemove(tokenToRemove, out result)) + return result; + + await Task.Delay(40).ConfigureAwait(false); + + tokenToRemove = AvailableTokens.FirstOrDefault().Value; + } + + return result; + } + finally + { + _semaphore.Release(); + } + } + } +} {{/netStandard}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index afd37d6d7987..28ffb2c9bb31 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -341,9 +341,10 @@ namespace {{packageName}}.{{apiPackage}} if (accept != null) request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); - {{/-first}}{{/produces}} - request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; - + {{/-first}}{{/produces}}{{^netStandard}} + request.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}};{{/netStandard}}{{#netStandard}} + request.Method = new HttpMethod("{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}}");{{/netStandard}} {{! early standard versions do not have HttpMethod.Patch }} + using (HttpResponseMessage responseMessage = await HttpClient.SendAsync(request, cancellationToken.GetValueOrDefault()).ConfigureAwait(false)) { DateTime requestedAt = DateTime.UtcNow; diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache index 265bf40550c9..b64731f81dd4 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api_test.mustache @@ -1,5 +1,6 @@ {{>partial_header}} using System; +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; using Microsoft.Extensions.DependencyInjection; @@ -36,10 +37,8 @@ namespace {{packageName}}.Test.Api {{#allParams}} {{{dataType}}} {{paramName}} = default; {{/allParams}} - {{#returnType}}var response = await {{/returnType}}_instance.{{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); - {{#returnType}} - Assert.IsType<{{{.}}}>(response); - {{/returnType}} + {{#returnType}}var response = {{/returnType}}await _instance.{{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}} + Assert.IsType<{{{.}}}>(response);{{/returnType}} } {{/operation}} {{/operations}} From 44f595595bd8bb8648a85d2512efaec937c69e5d Mon Sep 17 00:00:00 2001 From: devhl Date: Sat, 22 Jan 2022 17:01:47 -0500 Subject: [PATCH 31/35] added git_push.ps1 --- .../languages/CSharpNetCoreClientCodegen.java | 2 +- .../generichost/git_push.ps1.mustache | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index d84c4e896ce5..d6d08c2e0540 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -801,7 +801,7 @@ public void addGenericHostSupportingFiles(final String clientPackageDir, final S supportingFiles.add(new SupportingFile("HostConfiguration.mustache", clientPackageDir, "HostConfiguration.cs")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "docs" + File.separator + "scripts", "git_push.sh")); - // TODO: supportingFiles.add(new SupportingFile("git_push.ps1.mustache", "docs" + File.separator + "scripts", "git_push.ps1")); + supportingFiles.add(new SupportingFile("git_push.ps1.mustache", "docs" + File.separator + "scripts", "git_push.ps1")); // TODO: supportingFiles.add(new SupportingFile("generate.ps1.mustache", "docs" + File.separator + "scripts", "generate.ps1")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache new file mode 100644 index 000000000000..f6a57cf4f603 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache @@ -0,0 +1,57 @@ +param( + [Parameter()][Alias("h")][String]$GitHost = "{{{gitHost}}}", + [Parameter()][Alias("u")][String]$GitUserId = "{{{gitUserId}}}", + [Parameter()][Alias("r")][String]$GitRepoId = "{{{gitRepoId}}}", + [Parameter()][Alias("m")][string]$Message = "{{{releaseNote}}}" +) + +function Publish-ToGitHost{ + if ([string]::IsNullOrWhiteSpace($Message) -or $Message -eq "Minor update"){ + # it seems unlikely that we would want our git commit message to be the default, so lets prompt the user + $Message = Read-Host -Prompt "Please provide a commit message or press enter" + $Message = if([string]::IsNullOrWhiteSpace($Message)) { "no message provided" } else { $Message } + } + + git init + git add . + git commit -am "${Message}" + $branchName=$(git rev-parse --abbrev-ref HEAD) + + $gitRemote=$(git remote) + if([string]::IsNullOrWhiteSpace($gitRemote)){ + git remote add origin https://${GitHost}/${GitUserId}/${GitRepoId}.git + } + + Write-Output "Pulling from https://${GitHost}/${GitUserId}/${GitRepoId}.git" + git pull origin $branchName --ff-only + + if ($LastExitCode -ne 0){ + if (${GitHost} -eq "github.com"){ + Write-Output "The ${GitRepoId} repository may not exist yet. Creating it now with the GitHub CLI." + gh auth login --hostname github.com --web + gh repo create $GitRepoId --private + # sleep 2 seconds to ensure git finishes creation of the repo + Start-Sleep -Seconds 2 + } + else{ + throw "There was an issue pulling the origin branch. The remote repository may not exist yet." + } + } + + Write-Output "Pushing to https://${GitHost}/${GitUserId}/${GitRepoId}.git" + git push origin $branchName +} + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version 3.0 + +$rootPath=Resolve-Path -Path $PSScriptRoot/../.. + +Push-Location $rootPath + +try { + Publish-ToGitHost $GitHost $GitUserId $GitRepoId $Message +} +finally{ + Pop-Location +} \ No newline at end of file From 6250bb8a16301ac04fa345d8fc78bb0f607206c0 Mon Sep 17 00:00:00 2001 From: devhl Date: Sat, 22 Jan 2022 19:47:28 -0500 Subject: [PATCH 32/35] fixed bug in git_push.sh due to the new directory, prompting user for commit message --- .../generichost/git_push.ps1.mustache | 24 +++++++-- .../generichost/git_push.sh.mustache | 49 +++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.sh.mustache diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache index f6a57cf4f603..0f4084ef8179 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.ps1.mustache @@ -1,8 +1,9 @@ param( - [Parameter()][Alias("h")][String]$GitHost = "{{{gitHost}}}", + [Parameter()][Alias("g")][String]$GitHost = "{{{gitHost}}}", [Parameter()][Alias("u")][String]$GitUserId = "{{{gitUserId}}}", [Parameter()][Alias("r")][String]$GitRepoId = "{{{gitRepoId}}}", - [Parameter()][Alias("m")][string]$Message = "{{{releaseNote}}}" + [Parameter()][Alias("m")][string]$Message = "{{{releaseNote}}}", + [Parameter()][Alias("h")][switch]$Help ) function Publish-ToGitHost{ @@ -16,8 +17,8 @@ function Publish-ToGitHost{ git add . git commit -am "${Message}" $branchName=$(git rev-parse --abbrev-ref HEAD) - $gitRemote=$(git remote) + if([string]::IsNullOrWhiteSpace($gitRemote)){ git remote add origin https://${GitHost}/${GitUserId}/${GitRepoId}.git } @@ -45,6 +46,23 @@ function Publish-ToGitHost{ $ErrorActionPreference = "Stop" Set-StrictMode -Version 3.0 +if ($Help){ + Write-Output " + This script will initialize a git repository, then add and commit all files. + The local repository will then be pushed to your prefered git provider. + If the remote repository does not exist yet and you are using GitHub, + the repository will be created for you provided you have the GitHub CLI installed. + + Parameters: + -g | -GitHost -> ex: github.com + -m | -Message -> the git commit message + -r | -GitRepoId -> the name of the repository + -u | -GitUserId -> your user id + " + + return +} + $rootPath=Resolve-Path -Path $PSScriptRoot/../.. Push-Location $rootPath diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.sh.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.sh.mustache new file mode 100644 index 000000000000..3d4b710dc039 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/git_push.sh.mustache @@ -0,0 +1,49 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=${1:-{{{gitUserId}}}} +git_repo_id=${2:-{{{gitRepoId}}}} +release_note=${3:-{{{releaseNote}}}} +git_host=${4:-{{{gitHost}}}} + +starting_directory=$(pwd) +script_root="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +cd $script_root +cd ../.. + +if [ "$release_note" = "" ] || [ "$release_note" = "Minor update" ]; then + # it seems unlikely that we would want our git commit message to be the default, so lets prompt the user + echo "Please provide a commit message or press enter" + read user_input + release_note=$user_input + if [ "$release_note" = "" ]; then + release_note="no message provided" + fi +fi + +git init +git add . +git commit -am "$release_note" +branch_name=$(git rev-parse --abbrev-ref HEAD) +git_remote=$(git remote) + +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +echo "[INFO] Pulling from https://${git_host}/${git_user_id}/${git_repo_id}.git" +git pull origin $branch_name --ff-only + +echo "[INFO] Pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin $branch_name + +cd $starting_directory From ad8d0933814ac58e1dc7a5276c12b3b23168c856 Mon Sep 17 00:00:00 2001 From: devhl Date: Sat, 22 Jan 2022 20:39:45 -0500 Subject: [PATCH 33/35] moved documentation folders --- .../languages/CSharpNetCoreClientCodegen.java | 20 +++++++++++++++---- .../resources/csharp-netcore/api_doc.mustache | 2 +- .../csharp-netcore/model_doc.mustache | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java index d6d08c2e0540..fa6695f2fea6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java @@ -333,7 +333,11 @@ public CSharpNetCoreClientCodegen() { @Override public String apiDocFileFolder() { - return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); + if (GENERICHOST.equals(getLibrary())){ + return (outputFolder + "/" + apiDocPath + File.separatorChar + "apis").replace('/', File.separatorChar); + }else{ + return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); + } } @Override @@ -463,7 +467,11 @@ public void setNonPublicApi(final boolean nonPublicApi) { @Override public String modelDocFileFolder() { - return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); + if (GENERICHOST.equals(getLibrary())){ + return (outputFolder + "/" + modelDocPath + File.separator + "models").replace('/', File.separatorChar); + }else{ + return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar); + } } @Override @@ -702,16 +710,20 @@ public void processOpts() { supportingFiles.add(new SupportingFile("FileParameter.mustache", clientPackageDir, "FileParameter.cs")); typeMapping.put("file", "FileParameter"); addRestSharpSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); } else if (GENERICHOST.equals(getLibrary())){ addGenericHostSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); + additionalProperties.put("apiDocPath", apiDocPath + File.separatorChar + "apis"); + additionalProperties.put("modelDocPath", modelDocPath + File.separatorChar + "models"); } else{ addRestSharpSupportingFiles(clientPackageDir, packageFolder, excludeTests, testPackageFolder, testPackageName, modelPackageDir); + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); } - additionalProperties.put("apiDocPath", apiDocPath); - additionalProperties.put("modelDocPath", modelDocPath); addTestInstructions(); } diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/api_doc.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/api_doc.mustache index 1286829ad402..3a3870510c94 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/api_doc.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/api_doc.mustache @@ -128,7 +128,7 @@ Name | Type | Description | Notes {{/responses}} {{/responses.0}} -[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +[[Back to top]](#) [[Back to API list]](../{{#useGenericHost}}../{{/useGenericHost}}README.md#documentation-for-api-endpoints) [[Back to Model list]](../{{#useGenericHost}}../{{/useGenericHost}}README.md#documentation-for-models) [[Back to README]](../{{#useGenericHost}}../{{/useGenericHost}}README.md) {{/operation}} {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/model_doc.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/model_doc.mustache index babf25481c4e..3c7f8b2db810 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/model_doc.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/model_doc.mustache @@ -16,7 +16,7 @@ Name | Type | Description | Notes {{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#isReadOnly}}[readonly] {{/isReadOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} {{/vars}} -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) +[[Back to Model list]](../{{#useGenericHost}}../{{/useGenericHost}}README.md#documentation-for-models) [[Back to API list]](../{{#useGenericHost}}../{{/useGenericHost}}README.md#documentation-for-api-endpoints) [[Back to README]](../{{#useGenericHost}}../{{/useGenericHost}}README.md) {{/model}} {{/models}} From fa7b202062fe2b27162ac057f6d12277ee52be76 Mon Sep 17 00:00:00 2001 From: devhl Date: Mon, 24 Jan 2022 22:53:50 -0500 Subject: [PATCH 34/35] fixed bug when apiKey in query --- .../libraries/generichost/api.mustache | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index 28ffb2c9bb31..efff9dd061ac 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -256,11 +256,11 @@ namespace {{packageName}}.{{apiPackage}} request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{^required}} if ({{paramName}} != null) - request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}} + request.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}));{{/required}}{{/headerParams}}{{#formParams}}{{#-first}} MultipartContent multipartContent = new MultipartContent(); - request.Content = multipartContent;{{#formParams}}{{#-first}} + request.Content = multipartContent; List> formParams = new List>(); @@ -275,14 +275,14 @@ namespace {{packageName}}.{{apiPackage}} if ({{paramName}} != null) multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{/isFile}}{{/formParams}}{{#bodyParam}} - + if (({{paramName}} as object) is System.IO.Stream stream) request.Content = new StreamContent(stream); else request.Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject({{paramName}}, ClientUtils.JsonSerializerSettings));{{/bodyParam}}{{#authMethods}}{{#-first}} List tokens = new List();{{/-first}}{{#isApiKey}} - + ApiKeyToken apiKey = (ApiKeyToken) await ApiKeyProvider.GetAsync(cancellationToken).ConfigureAwait(false); tokens.Add(apiKey);{{#isKeyInHeader}} @@ -293,6 +293,8 @@ namespace {{packageName}}.{{apiPackage}} apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} + uriBuilder.Query = parseQueryString.ToString(); + {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}} @@ -322,7 +324,7 @@ namespace {{packageName}}.{{apiPackage}} signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}} - string[] contentTypes = new string[] { + string[] contentTypes = new string[] { {{/-first}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{#-last}} };{{/-last}}{{/consumes}}{{#consumes}}{{#-first}} From 0f6d741bb0aa78f233d9492bb2c4c69b946406fe Mon Sep 17 00:00:00 2001 From: devhl Date: Tue, 25 Jan 2022 23:52:41 -0500 Subject: [PATCH 35/35] bug fix --- .../csharp-netcore/libraries/generichost/api.mustache | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache index efff9dd061ac..880952325e61 100644 --- a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache +++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/generichost/api.mustache @@ -289,11 +289,13 @@ namespace {{packageName}}.{{apiPackage}} apiKey.UseInHeader(request, "{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}} - apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}");{{/isKeyInQuery}}{{#isKeyInCookie}} + apiKey.UseInQuery(request, uriBuilder, parseQueryString, "{{keyParamName}}"); - apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}");{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} + uriBuilder.Query = parseQueryString.ToString();{{/isKeyInQuery}}{{#isKeyInCookie}} - uriBuilder.Query = parseQueryString.ToString(); + apiKey.UseInCookie(request, parseQueryString, "{{keyParamName}}"); + + uriBuilder.Query = parseQueryString.ToString();{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} {{! below line must be after any UseInQuery calls, but before using the HttpSignatureToken}} request.RequestUri = uriBuilder.Uri;{{#authMethods}}{{#isBasicBasic}}