Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[csharp-netcore] Adding generic host library #10627

Merged
merged 47 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d71105a
Merge pull request #1 from OpenAPITools/master
devhl-labs Jun 2, 2021
36c514b
Merge pull request #2 from OpenAPITools/master
devhl-labs Jun 8, 2021
a0c6107
Merge branch 'OpenAPITools:master' into master
devhl-labs Jun 12, 2021
ece89e5
Merge branch 'OpenAPITools:master' into master
devhl-labs Jul 3, 2021
d2a46f6
Merge branch 'OpenAPITools:master' into master
devhl-labs Jul 5, 2021
3ca0921
Merge branch 'OpenAPITools:master' into master
devhl-labs Jul 15, 2021
c2a1a4c
Merge branch 'OpenAPITools:master' into master
devhl-labs Aug 3, 2021
3456183
Merge branch 'OpenAPITools:master' into master
devhl-labs Oct 15, 2021
7dfcdee
added generichost library
devhl-labs Oct 18, 2021
2a1c275
added templates
devhl-labs Oct 18, 2021
9a21519
added an event, improved docs, added logging
devhl-labs Oct 23, 2021
39af198
adding event args file
devhl-labs Oct 23, 2021
a4a1d4e
fixed hard coded package name
devhl-labs Oct 23, 2021
63f2915
added an AddTokens overload for a single token
devhl-labs Oct 23, 2021
a83fc74
changed api clients to singletons to support the event registration
devhl-labs Oct 23, 2021
f786322
Merge branch 'OpenAPITools:master' into master
devhl-labs Nov 7, 2021
28cdd4c
Merge branch 'master' into generic-host
devhl-labs Nov 7, 2021
2b5d5a6
build samples
devhl-labs Nov 7, 2021
4ea5722
log exceptions while executing api responded event
devhl-labs Nov 24, 2021
1f2937e
nrt bug fixes, dangling comma fix
devhl-labs Nov 24, 2021
fafe671
resolving comments
devhl-labs Nov 25, 2021
c7f6fe6
removed debugging lines
devhl-labs Nov 25, 2021
77ea9e8
refactored token provider
devhl-labs Dec 4, 2021
641a7b9
rate limit provider now default
devhl-labs Dec 4, 2021
b909944
updated readme, added ConfigureAwait(false)
devhl-labs Dec 19, 2021
e069979
DI fixes
devhl-labs Dec 21, 2021
deba536
removed a hard coded project name
devhl-labs Dec 21, 2021
b695cf7
fixed nrt bugs
devhl-labs Dec 21, 2021
0c1c382
improved NRT and .net 3.1 support
devhl-labs Dec 28, 2021
63a3950
renamed projectName to apiName, added cli option
devhl-labs Dec 29, 2021
6514b77
trying to avoid conflict
devhl-labs Dec 29, 2021
4e90817
set GenerateAssemlbyInfo to true
devhl-labs Dec 31, 2021
190a3aa
created docs/scripts folder
devhl-labs Dec 31, 2021
fb3f3f5
moved ApiTestsBase.cs to not get overwritten
devhl-labs Dec 31, 2021
a3a9a30
test fixes and improvements
devhl-labs Dec 31, 2021
4133334
fixed licenseId bug, updated readme
devhl-labs Dec 31, 2021
31f9629
Merge branch 'OpenAPITools:master' into master
devhl-labs Jan 3, 2022
cacf3dc
Merge branch 'master' into generic-host
devhl-labs Jan 3, 2022
c87c202
build samples
devhl-labs Jan 3, 2022
62c5b99
export docs
devhl-labs Jan 3, 2022
b106cf4
removed new language features
devhl-labs Jan 6, 2022
b45f066
added support for .net standard 2.0
devhl-labs Jan 8, 2022
44f5955
added git_push.ps1
devhl-labs Jan 22, 2022
6250bb8
fixed bug in git_push.sh due to the new directory, prompting user for…
devhl-labs Jan 23, 2022
ad8d093
moved documentation folders
devhl-labs Jan 23, 2022
fa7b202
fixed bug when apiKey in query
devhl-labs Jan 25, 2022
0f6d741
bug fix
devhl-labs Jan 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/generators/csharp-netcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|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|<dl><dt>**httpclient**</dt><dd>HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.)</dd><dt>**restsharp**</dt><dd>RestSharp (https://github.com/restsharp/RestSharp)</dd></dl>|restsharp|
|library|HTTP library template (sub-template) to use|<dl><dt>**generichost**</dt><dd>HttpClient with Generic Host dependency injection (https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host) (Experimental. Subject to breaking changes without notice.)</dd><dt>**httpclient**</dt><dd>HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. Subject to breaking changes without notice.)</dd><dt>**restsharp**</dt><dd>RestSharp (https://github.com/restsharp/RestSharp)</dd></dl>|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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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())) {
Expand Down Expand Up @@ -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<Boolean> 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"));
Expand Down Expand Up @@ -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"));
Expand All @@ -726,9 +745,51 @@ 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<Boolean> 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"));
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"));
}

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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}

using System;

namespace {{packageName}}.Client
{
/// <summary>
/// API Exception
/// </summary>
{{>visibility}} class ApiException : Exception
{
/// <summary>
/// The reason the api request failed
/// </summary>
public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }

/// <summary>
/// The HttpStatusCode
/// </summary>
public System.Net.HttpStatusCode StatusCode { get; }

/// <summary>
/// The raw data returned by the api
/// </summary>
public string RawContent { get; }

/// <summary>
/// Construct the ApiException from parts of the reponse
/// </summary>
/// <param name="reasonPhrase"></param>
/// <param name="statusCode"></param>
/// <param name="rawContent"></param>
public ApiException(string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent)
{
ReasonPhrase = reasonPhrase;

StatusCode = statusCode;

RawContent = rawContent;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}

using System;

namespace {{packageName}}.Client
{
/// <summary>
/// A token constructed from an apiKey.
/// </summary>
public class ApiKeyToken : TokenBase
{
private string _raw;

private bool _useCookieContainer;

/// <summary>
/// Constructs an ApiKeyToken object.
/// </summary>
/// <param name="tokenTimeOut"></param>
/// <param name="value"></param>
/// <param name="useCookieContainer"></param>
public ApiKeyToken(TimeSpan tokenTimeOut, string value, bool useCookieContainer = false) : base(tokenTimeOut)
{
_raw = value;

_useCookieContainer = useCookieContainer;
}
/// <summary>
/// Places the token in the cookie.
/// </summary>
/// <param name="request"></param>
/// <param name="cookieName"></param>
public virtual void UseInCookie(System.Net.Http.HttpRequestMessage request, string cookieName)
{
if (!_useCookieContainer)
request.Headers.Add("Cookie", $"{ cookieName }=_raw");
}

/// <summary>
/// Places the token in the header.
/// </summary>
/// <param name="request"></param>
/// <param name="headerName"></param>
public virtual void UseInHeader(System.Net.Http.HttpRequestMessage request, string headerName)
{
request.Headers.Add(headerName, _raw);
}

/// <summary>
/// Places the token in the query.
/// </summary>
/// <param name="request"></param>
/// <param name="uriBuilder"></param>
/// <param name="parseQueryString"></param>
/// <param name="parameterName"></param>
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}};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Net;

namespace {{packageName}}.Client
{
/// <summary>
/// Useful for tracking server health.
/// </summary>
public class ApiResponseEventArgs : EventArgs
{
/// <summary>
/// The time the request was sent.
/// </summary>
public DateTime RequestedAt { get; }
/// <summary>
/// The time the response was received.
/// </summary>
public DateTime ReceivedAt { get; }
/// <summary>
/// The HttpStatusCode received.
/// </summary>
public HttpStatusCode HttpStatus { get; }
/// <summary>
/// The path requested.
/// </summary>
public string Path { get; }
/// <summary>
/// The elapsed time from request to response.
/// </summary>
public TimeSpan ToTimeSpan => this.ReceivedAt - this.RequestedAt;

/// <summary>
/// The event args used to track server health.
/// </summary>
/// <param name="requestedAt"></param>
/// <param name="receivedAt"></param>
/// <param name="httpStatus"></param>
/// <param name="path"></param>
public ApiResponseEventArgs(DateTime requestedAt, DateTime receivedAt, HttpStatusCode httpStatus, string path)
{
RequestedAt = requestedAt;
ReceivedAt = receivedAt;
HttpStatus = httpStatus;
Path = path;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}

using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;

namespace {{packageName}}.Client
{
/// <summary>
/// Provides a non-generic contract for the ApiResponse wrapper.
/// </summary>
public interface IApiResponse
{
/// <summary>
/// The data type of <see cref="Data"/>
/// </summary>
Type ResponseType { get; }

/// <summary>
/// Gets or sets the status code (HTTP status code)
/// </summary>
/// <value>The status code.</value>
HttpStatusCode StatusCode { get; }

/// <summary>
/// The raw content of this response
/// </summary>
string RawContent { get; }
}

/// <summary>
/// API Response
/// </summary>
{{>visibility}} partial class ApiResponse<T> : IApiResponse
{
#region Properties

/// <summary>
/// The deserialized content
/// </summary>
public T{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} Content { get; set; }

/// <summary>
/// Gets or sets the status code (HTTP status code)
/// </summary>
/// <value>The status code.</value>
public HttpStatusCode StatusCode { get; }

/// <summary>
/// The content of this response
/// </summary>
public Type ResponseType
{
get { return typeof(T); }
}

/// <summary>
/// The raw data
/// </summary>
public string RawContent { get; }

/// <summary>
/// The IsSuccessStatusCode from the api response
/// </summary>
public bool IsSuccessStatusCode { get; }

/// <summary>
/// The reason phrase contained in the api response
/// </summary>
public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }

/// <summary>
/// The headers contained in the api response
/// </summary>
public System.Net.Http.Headers.HttpResponseHeaders Headers { get; }

#endregion Properties

/// <summary>
/// Construct the reponse using an HttpResponseMessage
/// </summary>
/// <param name="response"></param>
/// <param name="rawContent"></param>
public ApiResponse(System.Net.Http.HttpResponseMessage response, string rawContent)
{
StatusCode = response.StatusCode;
Headers = response.Headers;
IsSuccessStatusCode = response.IsSuccessStatusCode;
ReasonPhrase = response.ReasonPhrase;
RawContent = rawContent;
}
}
}
Loading