diff --git a/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java new file mode 100644 index 0000000000..6dfab8b057 --- /dev/null +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -0,0 +1,239 @@ +package io.swagger.codegen.v3.generators.dotnet; + +import io.swagger.codegen.v3.CliOption; +import io.swagger.codegen.v3.CodegenConstants; +import io.swagger.codegen.v3.CodegenModel; +import io.swagger.codegen.v3.CodegenOperation; +import io.swagger.codegen.v3.CodegenParameter; +import io.swagger.codegen.v3.CodegenProperty; +import io.swagger.codegen.v3.SupportingFile; +import io.swagger.codegen.v3.generators.DefaultCodegenConfig; +import io.swagger.v3.oas.models.media.Schema; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +public class CsharpDotnetCoreClientCodegen extends AbstractCSharpCodegen { + public static final String CLIENT_PACKAGE = "clientPackage"; + public static final String USE_CSPROJ_FILE = "useCsProjFile"; + public static final String DefaultTargetFramework = "net8.0"; + protected String clientPackage = "IO.Swagger.Client"; + protected String systemTextJsonVersion = "8.0.3"; + protected String apiDocPath = "docs/clients"; + protected String modelDocPath = "docs/models"; + + protected Map versions = new HashMap<>(); + + public CsharpDotnetCoreClientCodegen() { + super(); + + versions.put("net8.0","8.0.3"); + versions.put("net7.0","7.0.4"); + versions.put("net6.0","6.0.9"); + versions.put("net5.0","5.0.2"); + + importMapping.clear(); + + modelTemplateFiles.put("model.mustache", ".cs"); + apiTemplateFiles.put("api.mustache", ".cs"); + + setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotnetCore"); + + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, + "C# package name (convention: Camel.Case).") + .defaultValue(packageName)); + cliOptions.add(new CliOption(CodegenConstants.DOTNET_FRAMEWORK, + CodegenConstants.DOTNET_FRAMEWORK_DESC) + .defaultValue("net8.0")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, + "C# package version.") + .defaultValue(packageVersion)); + } + + @Override + public void processOpts() { + super.processOpts(); + + sourceFolder = ""; + + setApiPackage(packageName + ".Api"); + setModelPackage(packageName + ".Model"); + setClientPackage(packageName + ".Client"); + + if (additionalProperties.containsKey(CLIENT_PACKAGE)) { + setClientPackage((String) additionalProperties.get(CLIENT_PACKAGE)); + } else { + additionalProperties.put(CLIENT_PACKAGE, getClientPackage()); + } + + final String clientPackage = getClientPackage(); + + if(!additionalProperties.containsKey("apiDocPath")) { + additionalProperties.put("apiDocPath", apiDocPath); + } + if(!additionalProperties.containsKey("modelDocPath")) { + additionalProperties.put("modelDocPath", modelDocPath); + } + + String exceptionTypeName = clientPackage.replace(".", "") + "ApiException"; + additionalProperties.put("exceptionTypeName", exceptionTypeName); + String apiClientBaseTypeName = clientPackage.replace(".", "") + "ApiClientBase"; + additionalProperties.put("apiClientBaseTypeName", apiClientBaseTypeName); + + supportingFiles.add(new SupportingFile("ApiException.mustache", "", exceptionTypeName + ".cs")); + supportingFiles.add(new SupportingFile("ApiClient.mustache", "", apiClientBaseTypeName + ".cs")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + + if (additionalProperties.containsKey(USE_CSPROJ_FILE) && Boolean.parseBoolean(additionalProperties.get(USE_CSPROJ_FILE).toString())) { + supportingFiles.add(new SupportingFile("csproj.mustache", "", clientPackage + ".csproj")); + } + if(!additionalProperties.containsKey(CodegenConstants.DOTNET_FRAMEWORK)) { + additionalProperties.put(CodegenConstants.DOTNET_FRAMEWORK, DefaultTargetFramework); + } + String version = additionalProperties.get(CodegenConstants.DOTNET_FRAMEWORK).toString(); + boolean contains = versions.containsKey(version); + if(contains) { + setSystemTextJsonVersion(versions.get(version)); + } + } + + @Override + public String apiPackage() { + return packageName + ".Clients"; + } + + @Override + public String modelPackage() { + return packageName + ".Models"; + } + + public void setSystemTextJsonVersion(String systemTextJsonVersion){ + this.systemTextJsonVersion = systemTextJsonVersion; + } + + public String getSystemTextJsonVersion(){ + return this.systemTextJsonVersion; + } + + public String getClientPackage() { + return clientPackage; + } + + public void setClientPackage(String clientPackage) { + this.clientPackage = clientPackage; + } + + @Override + protected void processOperation(CodegenOperation operation) { + operation.httpMethod = DefaultCodegenConfig.camelize(operation.httpMethod.toLowerCase(), false); + + CodegenParameter cancellationTokenParameter = new CodegenParameter(); + cancellationTokenParameter.dataType = "CancellationToken"; + cancellationTokenParameter.paramName = "ct"; + cancellationTokenParameter.secondaryParam = true; + operation.getVendorExtensions() + .put("x-has-more", false); + + if(operation.allParams.size() != 0) { + CodegenParameter lastParameter = operation.allParams.get(operation.allParams.size() - 1); + lastParameter.getVendorExtensions() + .put("x-has-more", true); + } + + operation.allParams.add(cancellationTokenParameter); + + super.processOperation(operation); + } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property){ + } + + @Override + public String getTypeDeclaration(Schema propertySchema) { + String result = super.getTypeDeclaration(propertySchema); + int index = result.indexOf("List"); + while (index >= 0) { + if(result.length() == (index + "List".length())) { + result = replaceListToArrayListAtIndexes(result, index, index + "List".length()); + index += "ArrayList".length(); + } else { + char prevChar = '\u0000'; + if(index != 0){ + prevChar = result.charAt(index - 1); + } + char nextChar = result.charAt(index + "List".length()); + if( + (prevChar == ' ' || prevChar == ',' || prevChar == '<') + && (nextChar == ',' || nextChar == '>') + ) { + result = replaceListToArrayListAtIndexes(result, index, index + "List".length()); + index += "ArrayList".length(); + } + } + index = result.indexOf("List", index + 1); + } + return result; + } + + private String replaceListToArrayListAtIndexes(String input, int start, int end) { + return input.substring(0, start) + "ArrayList" + input.substring(end, input.length()); + } + + @Override + public io.swagger.codegen.v3.CodegenType getTag() { + return io.swagger.codegen.v3.CodegenType.CLIENT; + } + + @Override + public String getName() { + return "csharp-dotnet-core"; + } + + @Override + public String getHelp() { + return "Generates a C# dotnet Core client library."; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + "Clients"; + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + "Models"; + } + + @Override + public String apiDocFileFolder() { + return handleAbsolutePathIfPresentFromProperties("apiDocPath"); + } + + @Override + public String modelDocFileFolder() { + return handleAbsolutePathIfPresentFromProperties("modelDocPath"); + } + + private String handleAbsolutePathIfPresentFromProperties(String propertyWithPathName){ + String pathFromProperty = additionalProperties.get(propertyWithPathName).toString(); + return handleAbsolutePathIfPresent(pathFromProperty); + } + + private String handleAbsolutePathIfPresent(String value){ + String pathFromProperties = value; + Path path = Paths.get(pathFromProperties); + + if (path.isAbsolute()) { + return pathFromProperties.replace('/', File.separatorChar); + } + + return (outputFolder + "/" +pathFromProperties).replace('/', File.separatorChar); + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig b/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig index 2adb275cf1..792645487b 100644 --- a/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig +++ b/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig @@ -2,6 +2,7 @@ io.swagger.codegen.v3.generators.dart.DartClientCodegen io.swagger.codegen.v3.generators.dotnet.AspNetCoreServerCodegen io.swagger.codegen.v3.generators.dotnet.CSharpClientCodegen io.swagger.codegen.v3.generators.dotnet.CsharpDotNet2ClientCodegen +io.swagger.codegen.v3.generators.dotnet.CsharpDotnetCoreClientCodegen io.swagger.codegen.v3.generators.go.GoClientCodegen io.swagger.codegen.v3.generators.go.GoServerCodegen io.swagger.codegen.v3.generators.html.StaticDocCodegen diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache new file mode 100644 index 0000000000..a8b60a8c59 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -0,0 +1,458 @@ +using System; +using System.Collections.Generic; +using System.CodeDom.Compiler; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Globalization; +using System.Text; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace {{clientPackage}} +{ + /// + /// Base type for API client is mainly responsible for making the HTTP call to the API. + /// + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public abstract class {{apiClientBaseTypeName}} + { + protected readonly HttpClient _httpClient; + protected readonly string _basePath; + protected readonly JsonSerializerOptions _options; + + /// + /// Initializes a new instance of the class. + /// + /// Client for making http calls. + /// The base path. + /// Serialization settings. + protected {{apiClientBaseTypeName}}(HttpClient httpClient, string basePath, JsonSerializerOptions options = null) + { + _httpClient = httpClient; + _basePath = basePath; + _options = options ?? JsonSerializerOptions.Default; + } + + protected virtual string ContentType => "application/json-patch+json"; + protected virtual string Accept => "application/json"; + protected virtual IFormatProvider DateTimeFormatForQuery => CultureInfo.CurrentCulture.DateTimeFormat; + + /// + /// Makes the HTTP request. + /// + /// URL path. + /// HTTP method. + /// Query parameters. + /// Object to be serialized for http request body. + /// Header parameters. + /// Form parameters. + /// File parameters. + /// Operation cancellation token. + /// Result of request. + protected virtual async Task CallApi( + string path, + HttpMethod method, + Dictionary queryParams = null, + object body = null, + Dictionary headerParams = null, + Dictionary formParams = null, + Dictionary fileParams = null, + CancellationToken ct = default + ) + { + using (var request = new HttpRequestMessage()) + { + PrepareRequest(path, method, queryParams, body, headerParams, formParams, fileParams, request); + HttpResponseMessage response = null; + try + { + response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); + + var headers = CollectHeaders(response); + + var status = (int)response.StatusCode; + if (status is >= 200 and < 300) + { + return await DeserializeResponse(response, headers, ct).ConfigureAwait(false); + } + else + { + var responseData = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); + throw new {{exceptionTypeName}}(status, "The HTTP status code of the response was not expected (" + status + ").", responseData, headers, null); + } + } + finally + { + if(fileParams != null) + { + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } + } + response?.Dispose(); + } + } + } + + /// + /// Makes the HTTP request. + /// + /// URL path. + /// HTTP method. + /// Query parameters. + /// Object to be serialized for http request body. + /// Header parameters. + /// Form parameters. + /// File parameters. + /// Operation cancellation token. + /// Result of request. + protected virtual async Task CallApi( + string path, + HttpMethod method, + Dictionary queryParams = null, + object body = null, + Dictionary headerParams = null, + Dictionary formParams = null, + Dictionary fileParams = null, + CancellationToken ct = default + ) + { + using (var request = new HttpRequestMessage()) + { + PrepareRequest(path, method, queryParams, body, headerParams, formParams, fileParams, request); + + HttpResponseMessage response = null; + try + { + response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); + + var headers = CollectHeaders(response); + + var status = (int)response.StatusCode; + if (status is < 200 or >= 300) + { + var responseData = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); + throw new {{exceptionTypeName}}(status, "The HTTP status code of the response was not expected (" + status + ").", responseData, headers, null); + } + } + finally + { + if(fileParams != null) + { + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } + } + response?.Dispose(); + } + } + } + + private void PrepareRequest( + string path, + HttpMethod method, + Dictionary queryParams, + object body, + Dictionary headerParams, + Dictionary formParams, + Dictionary fileParams, + HttpRequestMessage request + ) + { + request.Method = method; + + // prepare request body + if (body != null) + { + string json; + if (body is string stringBody) + { + json = stringBody; + } + else + { + json = JsonSerializer.Serialize(body, _options); + } + + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType); + request.Content = content; + } + + //form-data + if (formParams != null && formParams.Count > 0) + { + request.Content = new FormUrlEncodedContent(formParams); + } + + // file sending + if(fileParams != null) + { + using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture))) + { + foreach(var kvp in fileParams) + { + content.Add(ToStreamContent(kvp.Value)); + } + request.Content = content; + } + } + + // headers + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(Accept)); + if (headerParams != null) + { + foreach (var kvp in headerParams) + { + request.Headers.Add(kvp.Key, kvp.Value); + } + } + + // build url + var urlBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(_basePath)) + { + urlBuilder.Append(_basePath); + } + + urlBuilder.Append(path); + + urlBuilder.Append('?'); + if (queryParams != null) + { + foreach (var kvp in queryParams) + { + urlBuilder.Append( + Uri.EscapeDataString(kvp.Key) + ).Append('=') + .Append( + Uri.EscapeDataString( + ConvertToString(kvp.Value, CultureInfo.InvariantCulture) + ) + ).Append('&'); + } + + urlBuilder.Length--; + } + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + } + + protected virtual async Task DeserializeResponse( + HttpResponseMessage response, + IReadOnlyDictionary> headers, + CancellationToken ct + ) + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false)) + { + return await JsonSerializer.DeserializeAsync(responseStream, _options, ct); + } + } + catch (JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new {{exceptionTypeName}}((int)response.StatusCode, message, string.Empty, headers, exception); + } + } + + protected virtual async Task DeserializeResponseAsString( + HttpResponseMessage response, + IReadOnlyDictionary> headers, + CancellationToken ct + ) + { + var responseText = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); + try + { + return JsonSerializer.Deserialize(responseText, _options); + } + catch (JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new {{exceptionTypeName}}((int)response.StatusCode, message, responseText, headers, exception); + } + } + + private string ConvertToString(object value, IFormatProvider cultureInfo) + { + if (value == null) + { + return String.Empty; + } + + if (value is Enum) + { + var name = Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = value.GetType().GetTypeInfo().GetDeclaredField(name); + if (field != null) + { + var attribute = field.GetCustomAttribute(typeof(System.Runtime.Serialization.EnumMemberAttribute)) as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value ?? name; + } + } + + var converted = Convert.ToString(Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted ?? string.Empty; + } + } + else if (value is bool asBool) + { + return Convert.ToString(asBool, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[] asByte) + { + return Convert.ToBase64String(asByte); + } + else if (value is string[] stringArray) + { + return string.Join(",", stringArray); + } + else if (value.GetType().IsArray) + { + var valueArray = (Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = Convert.ToString(value, cultureInfo); + return result ?? String.Empty; + } + + /// + /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. + /// If parameter is a list of string, join the list with ",". + /// Otherwise just return the string. + /// + /// The parameter (header, path, query, form). + /// Formatted string. + protected virtual string ParameterToString(object obj) + { + 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(DateTimeFormatForQuery); + if (obj is List list) + return String.Join(",", list.ToArray()); + return Convert.ToString(obj); + } + + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public static FileParameter ParameterToFile(string name, Stream stream) + { + if (stream is FileStream fs) + { + return new FileParameter(name, stream, Path.GetFileName(fs.Name)); + } + return new FileParameter(name, stream, "no_file_name_provided"); + } + + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public static FileParameter ParameterToFile(string name, byte[] data) + { + return new FileParameter(name, data, "no_file_name_provided"); + } + + private static Dictionary> CollectHeaders(HttpResponseMessage response) + { + var headers = new Dictionary>(response.Headers.Count() + response.Content.Headers.Count()); + foreach (var item in response.Headers) + { + headers[item.Key] = item.Value; + } + + foreach (var item in response.Content.Headers) + { + headers[item.Key] = item.Value; + } + + return headers; + } + + private StreamContent ToStreamContent(FileParameter fileParameter) + { + var stream = fileParameter.FileData; + var streamContent = new StreamContent(stream); + + streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileParameter.ContentType); + + var dispositionHeader = ContentDispositionHeaderValue.Parse($"form-data; name=\"{fileParameter.ParameterName}\"; filename=\"{fileParameter.FileName}\""); + streamContent.Headers.ContentDisposition = dispositionHeader; + + return streamContent; + } + } + + /// + /// Wrapper for HttpRequest parameter that is represented by file. + /// + public class FileParameter + { + /// + /// Wrapper for HttpRequest parameter that is represented by file. + /// + public FileParameter(string parameterName, Stream fileData, string fileName, string contentType = null) + { + ParameterName = parameterName; + FileName = fileName; + FileData = fileData; + ContentType = contentType; + } + + /// + /// Wrapper for HttpRequest parameter that is represented by file. + /// + public FileParameter(string parameterName, byte[] fileData, string fileName, string contentType = null) : this(parameterName, new MemoryStream(fileData), fileName, contentType) + { + } + + /// + /// Stream of file data. + /// + public Stream FileData { get; } + + /// + /// File name to be used in upload. + /// + public string FileName { get; } + + /// + /// Name of parameter that file will be passed as. + /// + public string ParameterName { get; } + + /// + /// MimeType for file. + /// + public string ContentType { get; } + } +} \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache new file mode 100644 index 0000000000..b8bec08fc7 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +namespace {{clientPackage}} +{ + /// + /// API Exception + /// + public class {{exceptionTypeName}} : Exception + { + /// + /// Gets or sets the error code (HTTP status code) + /// + /// The error code (HTTP status code). + public int ErrorCode { get; set; } + + /// + /// Gets or sets the error content (body json object) + /// + /// The error content (Http response body). + public Object ErrorContent { get; private set; } + + /// + /// Headers, used in failed request. + /// + public IReadOnlyDictionary> Headers {get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public {{exceptionTypeName}}() {} + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Error message. + public {{exceptionTypeName}}(int errorCode, string message) : this(errorCode, message, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Error message. + /// Error content. + public {{exceptionTypeName}}(int errorCode, string message, Object errorContent = null, IReadOnlyDictionary> headers = null, Exception original = null) : base(message, original) + { + ErrorCode = errorCode; + ErrorContent = errorContent; + Headers = headers; + } + + } +} \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/README.mustache b/src/main/resources/handlebars/csharp-dotnet-core/README.mustache new file mode 100644 index 0000000000..ff73f8877f --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/README.mustache @@ -0,0 +1,105 @@ +# {{packageName}} - the C# library for the {{appName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +This C# SDK is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: + +- API version: {{appVersion}} +- SDK version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + + +## Target frameworks supported +- net5.0+ + + +## Dependencies +- System.Text.Json + +Note: NuGet is downloaded by the mono compilation script and packages are installed with it. No dependency DLLs are bundled with this generator + + +## Installation +Run the following command to generate the DLL +- [Mac/Linux] `/bin/sh compile-mono.sh` +- [Windows] TODO + +Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces: +```csharp +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; +``` + +## Getting Started + +```csharp +using System; +using System.Diagnostics; +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; + +namespace Example +{ + public class {{operationId}}Example + { + public void main() + { + {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}} + + var apiInstance = new {{classname}}(new HttpClient()); + {{#allParams}} + {{#isPrimitiveType}} + var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{/allParams}} + + try + { + {{#summary}} + // {{{.}}} + {{/summary}} + {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + Debug.WriteLine(result);{{/returnType}} + } + catch (Exception e) + { + Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); + } + } + } +}{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} +``` + + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + + +## Documentation for Models + +{{#modelPackage}} +{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} +{{/modelPackage}} +{{^modelPackage}} +No model defined in this package +{{/modelPackage}} \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache new file mode 100644 index 0000000000..98c4b39582 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Text; +using System.Threading.Tasks; +using System.CodeDom.Compiler; +{{#hasImport}}using {{modelPackage}}; +{{/hasImport}} + +namespace {{package}} +{ + {{#operations}} + /// + /// Represents a collection of functions to interact with the API endpoints + /// + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public partial interface I{{classname}} + { + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + {{#allParams}}/// {{description}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} + {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/operation}} + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public partial class {{classname}} : {{apiClientBaseTypeName}}, I{{classname}} + { + /// + /// Initializes a new instance of the class. + /// + /// HttpClient to be used for calls. + /// Base url to be used for calls. + public {{classname}}(HttpClient httpClient, String basePath="{{{basePath}}}") : base(httpClient, basePath) + { + } + + {{#operation}} + /// + public async {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#allParams}}{{#required}}// verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) throw new {{exceptionTypeName}}(400, "Missing required parameter '{{paramName}}' when calling {{nickname}}"); + {{/required}}{{/allParams}} + var path_ = new StringBuilder("{{{path}}}"); {{#pathParams}} + path_ = path_.Replace("{{=<% %>=}}{<% baseName %>}<%={{ }}=%>", ParameterToString({{{paramName}}}));{{/pathParams}} + + {{#queryParams.0}}var queryParams = new Dictionary(); + {{/queryParams.0}}{{#headerParams.0}}var headerParams = new Dictionary(); + {{/headerParams.0}}{{#formParams.0}}var formParams = new Dictionary(); + {{/formParams.0}}{{#formParams.0}}var fileParams = new Dictionary(); + {{/formParams.0}}{{#queryParams}} + if ({{paramName}} != null) queryParams.Add("{{baseName}}", ParameterToString({{paramName}})); // query parameter{{/queryParams}}{{#headerParams}} + if ({{paramName}} != null) headerParams.Add("{{baseName}}", ParameterToString({{paramName}})); // header parameter{{/headerParams}}{{#formParams}} + if ({{paramName}} != null) {{#isFile}}fileParams.Add("{{baseName}}", ParameterToFile("{{baseName}}", {{paramName}}));{{/isFile}}{{^isFile}}formParams.Add("{{baseName}}", ParameterToString({{paramName}})); // form parameter{{/isFile}}{{/formParams}} + + {{#returnType}}var response = {{/returnType}}await CallApi{{#returnType}}<{{{returnType}}}>{{/returnType}}( + path_.ToString(), + HttpMethod.{{httpMethod}},{{#queryParams.0}} + queryParams: queryParams,{{/queryParams.0}}{{#bodyParam}} + body: {{paramName}}, {{/bodyParam}}{{#headerParams.0}} + headerParams: headerParams,{{/headerParams.0}} {{#formParams.0}} + formParams: formParams, + fileParams: fileParams,{{/formParams.0}} + ct: ct + );{{#returnType}} + return response;{{/returnType}} + } + + {{/operation}} + } + {{/operations}} +} \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache new file mode 100644 index 0000000000..7d769d750c --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache @@ -0,0 +1,81 @@ +# {{apiPackage}}.{{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{{basePath}}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +# **{{{operationId}}}** +> {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Example +```csharp +using System; +using System.Diagnostics; +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; + +namespace Example +{ + public class {{operationId}}Example + { + public void main() + { + var apiInstance = new {{classname}}(new HttpClient(), "http://my-service.srv"); + {{#allParams}} + {{#isPrimitiveType}} + var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{/allParams}} + + try + { + {{#summary}} + // {{{.}}} + {{/summary}} + {{#returnType}}{{returnType}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + Debug.WriteLine(result);{{/returnType}} + } + catch (Exception e) + { + Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); + } + } + } +} +``` + +### Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{{dataType}}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/src/main/resources/handlebars/csharp-dotnet-core/csproj.mustache b/src/main/resources/handlebars/csharp-dotnet-core/csproj.mustache new file mode 100644 index 0000000000..ec49e420ef --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/csproj.mustache @@ -0,0 +1,22 @@ + + + + {{targetFramework}} + {{clientPackage}} + + + + + {{packageName}} + {{packageVersion}} + {{packageAuthors}} + {{packageCompany}} + {{packageTitle}} + {{packageDescription}} + {{packageCopyright}} + + + + + + \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache new file mode 100644 index 0000000000..eea2a87dd8 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.CodeDom.Compiler; +using System.Text; +using System.Text.Json.Serialization; + +{{#models}} +{{#model}} +namespace {{package}} +{ + /// + /// {{description}} + /// + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public partial class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} + { + {{#vars}} + /// + /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}} + /// {{#description}} + /// {{{description}}}{{/description}} + [JsonPropertyName("{{baseName}}")] + public {{{datatype}}} {{name}} { get; set; } + + {{/vars}} + + /// + /// Get the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append("class {{classname}} {\n"); + {{#vars}} + sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); + {{/vars}} + sb.Append("}\n"); + return sb.ToString(); + } + + {{/model}} + {{/models}} + } +} \ No newline at end of file diff --git a/src/main/resources/handlebars/csharp-dotnet-core/model_doc.mustache b/src/main/resources/handlebars/csharp-dotnet-core/model_doc.mustache new file mode 100644 index 0000000000..e7c3ed7593 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/model_doc.mustache @@ -0,0 +1,14 @@ +{{#models}} +{{#model}} +# {{{modelPackage}}}.{{{classname}}} +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#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) + +{{/model}} +{{/models}}