From d54a30a129e786ff53b7c2161854e3d6840b5121 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Mon, 6 May 2024 17:59:12 +0300 Subject: [PATCH 01/13] feature: dotnet core client generator --- .../dotnet/AbstractCSharpCodegen.java | 7 +- .../dotnet/CsharpDotnetCoreClientCodegen.java | 200 +++++++++ .../handlebars/lambda/PascalCaseLambda.java | 60 +++ .../io.swagger.codegen.v3.CodegenConfig | 1 + .../csharp-dotnet-core/ApiClient.mustache | 405 ++++++++++++++++++ .../csharp-dotnet-core/ApiException.mustache | 51 +++ .../csharp-dotnet-core/README.mustache | 148 +++++++ .../csharp-dotnet-core/api.mustache | 79 ++++ .../csharp-dotnet-core/api_doc.mustache | 97 +++++ .../csharp-dotnet-core/csproj.mustache | 22 + .../csharp-dotnet-core/model.mustache | 44 ++ .../csharp-dotnet-core/model_doc.mustache | 14 + 12 files changed, 1123 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java create mode 100644 src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/README.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/api.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/csproj.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/model.mustache create mode 100644 src/main/resources/handlebars/csharp-dotnet-core/model_doc.mustache diff --git a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java index 8b846d9a36..bdb9cc0593 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java @@ -10,11 +10,7 @@ import io.swagger.codegen.v3.CodegenProperty; import io.swagger.codegen.v3.generators.DefaultCodegenConfig; import io.swagger.codegen.v3.generators.handlebars.csharp.CsharpHelper; -import io.swagger.codegen.v3.generators.handlebars.lambda.CamelCaseLambda; -import io.swagger.codegen.v3.generators.handlebars.lambda.IndentedLambda; -import io.swagger.codegen.v3.generators.handlebars.lambda.LowercaseLambda; -import io.swagger.codegen.v3.generators.handlebars.lambda.TitlecaseLambda; -import io.swagger.codegen.v3.generators.handlebars.lambda.UppercaseLambda; +import io.swagger.codegen.v3.generators.handlebars.lambda.*; import io.swagger.codegen.v3.generators.util.OpenAPIUtil; import io.swagger.codegen.v3.utils.ModelUtils; import io.swagger.codegen.v3.utils.URLPathUtil; @@ -348,6 +344,7 @@ private void addHandlebarsLambdas(Map objs) { .put("lowercase", new LowercaseLambda().generator(this)) .put("uppercase", new UppercaseLambda()) .put("titlecase", new TitlecaseLambda()) + .put("pascalcase", new PascalCaseLambda().generator(this)) .put("camelcase", new CamelCaseLambda().generator(this)) .put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true)) .put("indented", new IndentedLambda()) 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..398df74513 --- /dev/null +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -0,0 +1,200 @@ +package io.swagger.codegen.v3.generators.dotnet; + +import io.swagger.codegen.v3.*; + +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 DefaultargetFramwork = "net8.0"; + protected String clientPackage = "IO.Swagger.Client"; + protected String systemTextJsonVersion = "8.0.3"; + protected String apiDocPath = "docs"; + protected String modelDocPath = "docs"; + + protected Map versions = new HashMap<>(); + protected String exceptionTypeName; + protected String apiClientBaseTypeName; + + 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"); + + setApiPackage(packageName + ".Api"); + setModelPackage(packageName + ".Model"); + setClientPackage(packageName + ".Client"); + setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotNet2"); + + 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 = ""; + + if (additionalProperties.containsKey(CLIENT_PACKAGE)) { + setClientPackage((String) additionalProperties.get(CLIENT_PACKAGE)); + } else { + additionalProperties.put(CLIENT_PACKAGE, getClientPackage()); + } + + String generatorVersion = this.getClass().getPackage().getSpecificationVersion(); + if(generatorVersion == null) { + generatorVersion = "1.0"; + } + additionalProperties.put("generateorVersion", generatorVersion); + + 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", "", "ApiClient.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, DefaultargetFramwork); + } + 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) { + 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 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/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java b/src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java new file mode 100644 index 0000000000..3f18d68729 --- /dev/null +++ b/src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java @@ -0,0 +1,60 @@ +package io.swagger.codegen.v3.generators.handlebars.lambda; + +import com.github.jknack.handlebars.Lambda; +import io.swagger.codegen.v3.CodegenConfig; +import io.swagger.codegen.v3.generators.DefaultCodegenConfig; + +import java.io.IOException; + +/** + * Converts text in a fragment to PascalCase. + * + * Register: + *
+ * additionalProperties.put("pascalcase", new PascalCaseLambda());
+ * 
+ * + * Use: + *
+ * {{#pascalcase}}{{name}}{{/pascalcase}}
+ * 
+ */ +public class PascalCaseLambda implements Lambda { + + private CodegenConfig generator = null; + private Boolean escapeParam = false; + + public PascalCaseLambda() { + + } + + public PascalCaseLambda generator(final CodegenConfig generator) { + this.generator = generator; + return this; + } + + public PascalCaseLambda escapeAsParamName(final Boolean escape) { + this.escapeParam = escape; + return this; + } + @Override + public Object apply(Object o, com.github.jknack.handlebars.Template template) throws IOException { + String executed = template.apply(o); + String text = DefaultCodegenConfig.camelize(executed, false); + if (generator != null) { + text = ((DefaultCodegenConfig)generator).sanitizeName(text); + if (generator.reservedWords().contains(text)) { + // Escaping must be done *after* camelize, because generators may escape using characters removed by camelize function. + text = generator.escapeReservedWord(text); + } + + if (escapeParam) { + // NOTE: many generators call escapeReservedWord in toParamName, but we can't assume that's always the case. + // Here, we'll have to accept that we may be duplicating some work. + text = generator.toParamName(text); + } + } + return text; + } + +} 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..442ddf2ee3 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -0,0 +1,405 @@ +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", "{{generateorVersion}}")] +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, + object body, + Dictionary headerParams, + Dictionary formParams, + Dictionary fileParams, + CancellationToken ct + ) + { + 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 ReadObjectResponseAsync(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 + { + 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, + object body, + Dictionary headerParams, + Dictionary formParams, + Dictionary fileParams, + CancellationToken ct + ) + { + 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 + { + 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); + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async Task ReadObjectResponseAsync( + HttpResponseMessage response, + IReadOnlyDictionary> headers, + CancellationToken ct + ) + { + if (ReadResponseAsString) + { + 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); + } + } + 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); + } + } + + 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"); + } + + 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; + } +} + +public class FileParameter +{ + public FileParameter(string parameterName, Stream fileData, string fileName, string contentType = null) + { + ParameterName = parameterName; + FileName = fileName; + FileData = fileData; + ContentType = contentType; + } + + public FileParameter(string parameterName, byte[] fileData, string fileName, string contentType = null) : this(parameterName, new MemoryStream(fileData), fileName, contentType) + { + } + + public Stream FileData { get; } + public string FileName { get; } + public string ParameterName { get; } + 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..ff5b4476a0 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache @@ -0,0 +1,51 @@ +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; } + + 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; + } + +} 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..0d10db5b59 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/README.mustache @@ -0,0 +1,148 @@ +# {{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}} + + +## Frameworks supported +- .NET 2.0 + + +## Dependencies +- Mono compiler +- Newtonsoft.Json.7.0.1 +- RestSharp.Net2.1.1.11 + +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}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} + // Configure HTTP basic authorization: {{{name}}} + Configuration.Default.Username = "YOUR_USERNAME"; + Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); + // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed + // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} + {{/hasAuthMethods}} + + var apiInstance = new {{classname}}(); + {{#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}} + + +## 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}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} 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..af4ebc5502 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Text; +using System.Threading.Tasks; +using System.CodeDom.Compiler; +using {{clientPackage}}; +{{#hasImport}}using {{modelPackage}}; +{{/hasImport}} + +namespace {{apiPackage}}; + +{{#operations}} +/// +/// Represents a collection of functions to interact with the API endpoints +/// +[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +public partial interface I{{classname}} +{ + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + {{#allParams}}/// {{description}} + {{/allParams}}/// Operation cancellation token. + /// {{#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", "{{generateorVersion}}")] +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}} + + var queryParams = new Dictionary(); + var headerParams = new Dictionary(); + var formParams = new Dictionary(); + var fileParams = new Dictionary(); + object postBody = null; + + {{#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}}{{#bodyParam}}postBody = {{paramName}}; // http body (model) parameter + {{/bodyParam}} + + {{#returnType}}var response = {{/returnType}}await CallApi{{#returnType}}<{{{returnType}}}>{{/returnType}}(path_.ToString(), HttpMethod.{{#lambda.pascalcase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.pascalcase}}, queryParams, postBody, headerParams, formParams, fileParams, ct);{{#returnType}} + + return response;{{/returnType}} + } + + {{/operation}} +} +{{/operations}} 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..d8a074e11f --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache @@ -0,0 +1,97 @@ +# {{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() + { + {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} + // Configure HTTP basic authorization: {{{name}}} + Configuration.Default.Username = "YOUR_USERNAME"; + Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); + // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed + // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} + {{/hasAuthMethods}} + + var apiInstance = new {{classname}}(); + {{#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}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### 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..ccfeb77db9 --- /dev/null +++ b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.CodeDom.Compiler; +using System.Text; +using System.Text.Json.Serialization; + +{{#models}} +{{#model}} +namespace {{modelPackage}}; + +/// +/// {{description}} +/// +[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +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}} +} 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}} From 4b7af4afb09f87ed8cad789cfff4a046cd8eee21 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Tue, 7 May 2024 22:58:40 +0300 Subject: [PATCH 02/13] fix sevral invalid code generation cases --- .../dotnet/CsharpDotnetCoreClientCodegen.java | 30 +++++++++++++------ .../csharp-dotnet-core/ApiClient.mustache | 13 +++++++- .../csharp-dotnet-core/api.mustache | 4 +-- .../csharp-dotnet-core/model.mustache | 3 +- 4 files changed, 37 insertions(+), 13 deletions(-) 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 index 398df74513..6e278ad2d3 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -1,6 +1,8 @@ package io.swagger.codegen.v3.generators.dotnet; import io.swagger.codegen.v3.*; +import io.swagger.codegen.v3.generators.DefaultCodegenConfig; +import io.swagger.v3.oas.models.media.Schema; import java.io.File; import java.nio.file.Path; @@ -37,7 +39,7 @@ public CsharpDotnetCoreClientCodegen() { setApiPackage(packageName + ".Api"); setModelPackage(packageName + ".Model"); setClientPackage(packageName + ".Client"); - setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotNet2"); + setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotnetCore"); modelDocTemplateFiles.put("model_doc.mustache", ".md"); apiDocTemplateFiles.put("api_doc.mustache", ".md"); @@ -66,12 +68,6 @@ public void processOpts() { additionalProperties.put(CLIENT_PACKAGE, getClientPackage()); } - String generatorVersion = this.getClass().getPackage().getSpecificationVersion(); - if(generatorVersion == null) { - generatorVersion = "1.0"; - } - additionalProperties.put("generateorVersion", generatorVersion); - final String clientPackage = getClientPackage(); if(!additionalProperties.containsKey("apiDocPath")) { @@ -131,21 +127,37 @@ public void setClientPackage(String 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); + 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); + 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); + if(result.equals("Dictionary")) { + result = "Dictionary"; + } + return result; + } @Override public io.swagger.codegen.v3.CodegenType getTag() { diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache index 442ddf2ee3..a20659ebfc 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -17,7 +17,7 @@ namespace {{clientPackage}}; /// /// Base type for API client is mainly responsible for making the HTTP call to the API. /// -[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +[GeneratedCode("swagger-codegen", "{{generatorVersion}}")] public abstract class {{apiClientBaseTypeName}} { protected readonly HttpClient _httpClient; @@ -354,6 +354,17 @@ public abstract class {{apiClientBaseTypeName}} 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()); diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache index af4ebc5502..ebed8c4a5e 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -15,7 +15,7 @@ namespace {{apiPackage}}; /// /// Represents a collection of functions to interact with the API endpoints /// -[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +[GeneratedCode("swagger-codegen", "{{generatorVersion}}")] public partial interface I{{classname}} { {{#operation}} @@ -32,7 +32,7 @@ public partial interface I{{classname}} /// /// Represents a collection of functions to interact with the API endpoints /// -[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +[GeneratedCode("swagger-codegen", "{{generatorVersion}}")] public partial class {{classname}} : {{apiClientBaseTypeName}}, I{{classname}} { /// diff --git a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache index ccfeb77db9..2531738b42 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.CodeDom.Compiler; using System.Text; @@ -11,7 +12,7 @@ namespace {{modelPackage}}; /// /// {{description}} /// -[GeneratedCode("swagger-codegen", "{{generateorVersion}}")] +[GeneratedCode("swagger-codegen", "{{generatorVersion}}")] public partial class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} { {{#vars}} From e2aba4e24f92a30eae86401f88d10800a1f22b48 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Wed, 8 May 2024 17:26:21 +0300 Subject: [PATCH 03/13] [c#] modified non-generic List usage detection(can explode now), removed unused allocations in templates --- .../dotnet/CsharpDotnetCoreClientCodegen.java | 26 +++++++++++- .../csharp-dotnet-core/ApiClient.mustache | 40 +++++++++++++------ .../csharp-dotnet-core/api.mustache | 32 ++++++++------- 3 files changed, 69 insertions(+), 29 deletions(-) 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 index 6e278ad2d3..e0e067da58 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -153,12 +153,34 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert @Override public String getTypeDeclaration(Schema propertySchema) { String result = super.getTypeDeclaration(propertySchema); - if(result.equals("Dictionary")) { - result = "Dictionary"; + 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; diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache index a20659ebfc..7a50a7647a 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -56,12 +56,12 @@ public abstract class {{apiClientBaseTypeName}} protected virtual async Task CallApi( string path, HttpMethod method, - Dictionary queryParams, - object body, - Dictionary headerParams, - Dictionary formParams, - Dictionary fileParams, - CancellationToken ct + Dictionary queryParams = null, + object body = null, + Dictionary headerParams = null, + Dictionary formParams = null, + Dictionary fileParams = null, + CancellationToken ct = default ) { using (var request = new HttpRequestMessage()) @@ -87,6 +87,13 @@ public abstract class {{apiClientBaseTypeName}} } finally { + if(fileParams != null) + { + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } + } response?.Dispose(); } } @@ -106,13 +113,13 @@ public abstract class {{apiClientBaseTypeName}} /// Result of request. protected virtual async Task CallApi( string path, - HttpMethod method, - Dictionary queryParams, - object body, - Dictionary headerParams, - Dictionary formParams, - Dictionary fileParams, - CancellationToken ct + 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()) @@ -135,6 +142,13 @@ public abstract class {{apiClientBaseTypeName}} } finally { + if(fileParams != null) + { + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } + } response?.Dispose(); } } diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache index ebed8c4a5e..d415102f38 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -23,8 +23,7 @@ public partial interface I{{classname}} /// {{summary}} {{notes}} /// {{#allParams}}/// {{description}} - {{/allParams}}/// Operation cancellation token. - /// {{#returnType}}{{returnType}}{{/returnType}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); {{/operation}} } @@ -48,29 +47,34 @@ public partial class {{classname}} : {{apiClientBaseTypeName}}, I{{classname}} /// 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 + {{#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}} - var queryParams = new Dictionary(); - var headerParams = new Dictionary(); - var formParams = new Dictionary(); - var fileParams = new Dictionary(); - object postBody = null; + {{#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}}{{#bodyParam}}postBody = {{paramName}}; // http body (model) parameter - {{/bodyParam}} + {{/formParams}} - {{#returnType}}var response = {{/returnType}}await CallApi{{#returnType}}<{{{returnType}}}>{{/returnType}}(path_.ToString(), HttpMethod.{{#lambda.pascalcase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.pascalcase}}, queryParams, postBody, headerParams, formParams, fileParams, ct);{{#returnType}} - + {{#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}} } From 2368b31ff72f80095bad0554f88dc87457022a2a Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Wed, 8 May 2024 18:02:58 +0300 Subject: [PATCH 04/13] whtespace management and indent fixing --- .../handlebars/csharp-dotnet-core/api.mustache | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache index d415102f38..e573aaf155 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -50,21 +50,18 @@ public partial class {{classname}} : {{apiClientBaseTypeName}}, I{{classname}} {{#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}} + 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}} - + {{/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}} From b78735c331fcc53ff03f63983702efcc9fb742c6 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Wed, 8 May 2024 22:18:04 +0300 Subject: [PATCH 05/13] fix namespaces --- .../generators/dotnet/CsharpDotnetCoreClientCodegen.java | 9 +++++---- .../resources/handlebars/csharp-dotnet-core/api.mustache | 3 +-- .../handlebars/csharp-dotnet-core/model.mustache | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) 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 index e0e067da58..f63953c465 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -36,9 +36,6 @@ public CsharpDotnetCoreClientCodegen() { modelTemplateFiles.put("model.mustache", ".cs"); apiTemplateFiles.put("api.mustache", ".cs"); - setApiPackage(packageName + ".Api"); - setModelPackage(packageName + ".Model"); - setClientPackage(packageName + ".Client"); setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotnetCore"); modelDocTemplateFiles.put("model_doc.mustache", ".md"); @@ -62,6 +59,10 @@ public void processOpts() { sourceFolder = ""; + setApiPackage(packageName + ".Api"); + setModelPackage(packageName + ".Model"); + setClientPackage(packageName + ".Client"); + if (additionalProperties.containsKey(CLIENT_PACKAGE)) { setClientPackage((String) additionalProperties.get(CLIENT_PACKAGE)); } else { @@ -83,7 +84,7 @@ public void processOpts() { additionalProperties.put("apiClientBaseTypeName", apiClientBaseTypeName); supportingFiles.add(new SupportingFile("ApiException.mustache", "", exceptionTypeName + ".cs")); - supportingFiles.add(new SupportingFile("ApiClient.mustache", "", "ApiClient.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())) { diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache index e573aaf155..0bd95683b0 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -5,11 +5,10 @@ using System.Threading; using System.Text; using System.Threading.Tasks; using System.CodeDom.Compiler; -using {{clientPackage}}; {{#hasImport}}using {{modelPackage}}; {{/hasImport}} -namespace {{apiPackage}}; +namespace {{package}}; {{#operations}} /// diff --git a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache index 2531738b42..0f580101b9 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache @@ -7,7 +7,7 @@ using System.Text.Json.Serialization; {{#models}} {{#model}} -namespace {{modelPackage}}; +namespace {{package}}; /// /// {{description}} From 753678c643cf80f26244b18b48c1ff602025668c Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Mon, 13 May 2024 11:04:24 +0300 Subject: [PATCH 06/13] remove unused code --- .../dotnet/AbstractCSharpCodegen.java | 1 - .../handlebars/lambda/PascalCaseLambda.java | 60 ------------------- 2 files changed, 61 deletions(-) delete mode 100644 src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java diff --git a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java index bdb9cc0593..ab36660a84 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java @@ -344,7 +344,6 @@ private void addHandlebarsLambdas(Map objs) { .put("lowercase", new LowercaseLambda().generator(this)) .put("uppercase", new UppercaseLambda()) .put("titlecase", new TitlecaseLambda()) - .put("pascalcase", new PascalCaseLambda().generator(this)) .put("camelcase", new CamelCaseLambda().generator(this)) .put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true)) .put("indented", new IndentedLambda()) diff --git a/src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java b/src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java deleted file mode 100644 index 3f18d68729..0000000000 --- a/src/main/java/io/swagger/codegen/v3/generators/handlebars/lambda/PascalCaseLambda.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.swagger.codegen.v3.generators.handlebars.lambda; - -import com.github.jknack.handlebars.Lambda; -import io.swagger.codegen.v3.CodegenConfig; -import io.swagger.codegen.v3.generators.DefaultCodegenConfig; - -import java.io.IOException; - -/** - * Converts text in a fragment to PascalCase. - * - * Register: - *
- * additionalProperties.put("pascalcase", new PascalCaseLambda());
- * 
- * - * Use: - *
- * {{#pascalcase}}{{name}}{{/pascalcase}}
- * 
- */ -public class PascalCaseLambda implements Lambda { - - private CodegenConfig generator = null; - private Boolean escapeParam = false; - - public PascalCaseLambda() { - - } - - public PascalCaseLambda generator(final CodegenConfig generator) { - this.generator = generator; - return this; - } - - public PascalCaseLambda escapeAsParamName(final Boolean escape) { - this.escapeParam = escape; - return this; - } - @Override - public Object apply(Object o, com.github.jknack.handlebars.Template template) throws IOException { - String executed = template.apply(o); - String text = DefaultCodegenConfig.camelize(executed, false); - if (generator != null) { - text = ((DefaultCodegenConfig)generator).sanitizeName(text); - if (generator.reservedWords().contains(text)) { - // Escaping must be done *after* camelize, because generators may escape using characters removed by camelize function. - text = generator.escapeReservedWord(text); - } - - if (escapeParam) { - // NOTE: many generators call escapeReservedWord in toParamName, but we can't assume that's always the case. - // Here, we'll have to accept that we may be duplicating some work. - text = generator.toParamName(text); - } - } - return text; - } - -} From aa9f34ae9fbaa73d979a34d87f23c2b5230ef4f4 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Mon, 13 May 2024 11:11:25 +0300 Subject: [PATCH 07/13] revert imports corruption --- .../codegen/v3/generators/dotnet/AbstractCSharpCodegen.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java index ab36660a84..8b846d9a36 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/AbstractCSharpCodegen.java @@ -10,7 +10,11 @@ import io.swagger.codegen.v3.CodegenProperty; import io.swagger.codegen.v3.generators.DefaultCodegenConfig; import io.swagger.codegen.v3.generators.handlebars.csharp.CsharpHelper; -import io.swagger.codegen.v3.generators.handlebars.lambda.*; +import io.swagger.codegen.v3.generators.handlebars.lambda.CamelCaseLambda; +import io.swagger.codegen.v3.generators.handlebars.lambda.IndentedLambda; +import io.swagger.codegen.v3.generators.handlebars.lambda.LowercaseLambda; +import io.swagger.codegen.v3.generators.handlebars.lambda.TitlecaseLambda; +import io.swagger.codegen.v3.generators.handlebars.lambda.UppercaseLambda; import io.swagger.codegen.v3.generators.util.OpenAPIUtil; import io.swagger.codegen.v3.utils.ModelUtils; import io.swagger.codegen.v3.utils.URLPathUtil; From 376adaf76436bcd218e44d555b8c227a74984680 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 10:28:06 +0300 Subject: [PATCH 08/13] minor refactor --- .../dotnet/CsharpDotnetCoreClientCodegen.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 index f63953c465..6dfab8b057 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/dotnet/CsharpDotnetCoreClientCodegen.java @@ -1,6 +1,12 @@ package io.swagger.codegen.v3.generators.dotnet; -import io.swagger.codegen.v3.*; +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; @@ -13,15 +19,13 @@ public class CsharpDotnetCoreClientCodegen extends AbstractCSharpCodegen { public static final String CLIENT_PACKAGE = "clientPackage"; public static final String USE_CSPROJ_FILE = "useCsProjFile"; - public static final String DefaultargetFramwork = "net8.0"; + public static final String DefaultTargetFramework = "net8.0"; protected String clientPackage = "IO.Swagger.Client"; protected String systemTextJsonVersion = "8.0.3"; - protected String apiDocPath = "docs"; - protected String modelDocPath = "docs"; + protected String apiDocPath = "docs/clients"; + protected String modelDocPath = "docs/models"; protected Map versions = new HashMap<>(); - protected String exceptionTypeName; - protected String apiClientBaseTypeName; public CsharpDotnetCoreClientCodegen() { super(); @@ -91,7 +95,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("csproj.mustache", "", clientPackage + ".csproj")); } if(!additionalProperties.containsKey(CodegenConstants.DOTNET_FRAMEWORK)) { - additionalProperties.put(CodegenConstants.DOTNET_FRAMEWORK, DefaultargetFramwork); + additionalProperties.put(CodegenConstants.DOTNET_FRAMEWORK, DefaultTargetFramework); } String version = additionalProperties.get(CodegenConstants.DOTNET_FRAMEWORK).toString(); boolean contains = versions.containsKey(version); From 49ceb0f1451bc0d2be6464b977987dae40d6c816 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 10:59:59 +0300 Subject: [PATCH 09/13] unify templates for net5 --- .../csharp-dotnet-core/ApiClient.mustache | 648 +++++++++--------- .../csharp-dotnet-core/ApiException.mustache | 81 +-- .../csharp-dotnet-core/README.mustache | 57 +- .../csharp-dotnet-core/api.mustache | 119 ++-- 4 files changed, 434 insertions(+), 471 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache index 7a50a7647a..49b89425de 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -12,419 +12,421 @@ 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}} +namespace {{clientPackage}} { - protected readonly HttpClient _httpClient; - protected readonly string _basePath; - protected readonly JsonSerializerOptions _options; /// - /// Initializes a new instance of the class. + /// Base type for API client is mainly responsible for making the HTTP call to the API. /// - /// Client for making http calls. - /// The base path. - /// Serialization settings. - protected {{apiClientBaseTypeName}}(HttpClient httpClient, string basePath, JsonSerializerOptions options = null) + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public abstract class {{apiClientBaseTypeName}} { - _httpClient = httpClient; - _basePath = basePath; - _options = options ?? JsonSerializerOptions.Default; - } + protected readonly HttpClient _httpClient; + protected readonly string _basePath; + protected readonly JsonSerializerOptions _options; - protected virtual string ContentType => "application/json-patch+json"; - protected virtual string Accept => "application/json"; - protected virtual IFormatProvider DateTimeFormatForQuery => CultureInfo.CurrentCulture.DateTimeFormat; + /// + /// 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; + } - /// - /// 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()) + 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 + ) { - PrepareRequest(path, method, queryParams, body, headerParams, formParams, fileParams, request); - HttpResponseMessage response = null; - try + using (var request = new HttpRequestMessage()) { - response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); + 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 headers = CollectHeaders(response); - var status = (int)response.StatusCode; - if (status is >= 200 and < 300) - { - return await ReadObjectResponseAsync(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); + var status = (int)response.StatusCode; + if (status is >= 200 and < 300) + { + return await ReadObjectResponseAsync(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) + finally { - foreach(var fileParam in fileParams) + if(fileParams != null) { - fileParam.Value.FileData?.Dispose(); + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } } + response?.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()) + /// + /// 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 + ) { - PrepareRequest(path, method, queryParams, body, headerParams, formParams, fileParams, request); - - HttpResponseMessage response = null; - try + using (var request = new HttpRequestMessage()) { - response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); + PrepareRequest(path, method, queryParams, body, headerParams, formParams, fileParams, request); - var headers = CollectHeaders(response); - - var status = (int)response.StatusCode; - if (status is < 200 or >= 300) + HttpResponseMessage response = null; + try { - 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); + 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) + finally { - foreach(var fileParam in fileParams) + if(fileParams != null) { - fileParam.Value.FileData?.Dispose(); + foreach(var fileParam in fileParams) + { + fileParam.Value.FileData?.Dispose(); + } } + response?.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) + private void PrepareRequest( + string path, + HttpMethod method, + Dictionary queryParams, + object body, + Dictionary headerParams, + Dictionary formParams, + Dictionary fileParams, + HttpRequestMessage request + ) { - string json; - if (body is string stringBody) + request.Method = method; + + // prepare request body + if (body != null) { - json = stringBody; + 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; } - else + + //form-data + if (formParams != null && formParams.Count > 0) { - json = JsonSerializer.Serialize(body, _options); + request.Content = new FormUrlEncodedContent(formParams); } - - 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))) + // file sending + if(fileParams != null) { - foreach(var kvp in fileParams) + using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture))) { - content.Add(ToStreamContent(kvp.Value)); + foreach(var kvp in fileParams) + { + content.Add(ToStreamContent(kvp.Value)); + } + request.Content = content; } - request.Content = content; } - } - // headers - request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(Accept)); - if (headerParams != null) - { - foreach (var kvp in headerParams) + // headers + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(Accept)); + if (headerParams != null) { - request.Headers.Add(kvp.Key, kvp.Value); + 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); - } + // build url + var urlBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(_basePath)) + { + urlBuilder.Append(_basePath); + } - urlBuilder.Append(path); + urlBuilder.Append(path); - urlBuilder.Append('?'); - if (queryParams != null) - { - foreach (var kvp in queryParams) + urlBuilder.Append('?'); + if (queryParams != null) { - urlBuilder.Append( - Uri.EscapeDataString(kvp.Key) - ).Append('=') - .Append( - Uri.EscapeDataString( - ConvertToString(kvp.Value, CultureInfo.InvariantCulture) - ) - ).Append('&'); - } + foreach (var kvp in queryParams) + { + urlBuilder.Append( + Uri.EscapeDataString(kvp.Key) + ).Append('=') + .Append( + Uri.EscapeDataString( + ConvertToString(kvp.Value, CultureInfo.InvariantCulture) + ) + ).Append('&'); + } - urlBuilder.Length--; - } + urlBuilder.Length--; + } - var url = urlBuilder.ToString(); - request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); - } + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + } - public bool ReadResponseAsString { get; set; } + public bool ReadResponseAsString { get; set; } - protected virtual async Task ReadObjectResponseAsync( - HttpResponseMessage response, - IReadOnlyDictionary> headers, - CancellationToken ct - ) - { - if (ReadResponseAsString) + protected virtual async Task ReadObjectResponseAsync( + HttpResponseMessage response, + IReadOnlyDictionary> headers, + CancellationToken ct + ) { - var responseText = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); + if (ReadResponseAsString) + { + 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); + } + } try { - return JsonSerializer.Deserialize(responseText, _options); + 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 string as " + typeof(T).FullName + "."; - throw new {{exceptionTypeName}}((int)response.StatusCode, message, responseText, headers, 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); } } - try + + private string ConvertToString(object value, IFormatProvider cultureInfo) { - using (var responseStream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false)) + if (value == null) { - return await JsonSerializer.DeserializeAsync(responseStream, _options, ct); + return String.Empty; } - } - 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); - } - } - - 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) + if (value is Enum) { - var field = value.GetType().GetTypeInfo().GetDeclaredField(name); - if (field != null) + var name = Enum.GetName(value.GetType(), value); + if (name != null) { - var attribute = field.GetCustomAttribute(typeof(System.Runtime.Serialization.EnumMemberAttribute)) as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) + var field = value.GetType().GetTypeInfo().GetDeclaredField(name); + if (field != null) { - return attribute.Value ?? name; + 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; + 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; } - 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) + + /// + /// 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) { - return string.Join(",", stringArray); + 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); } - else if (value.GetType().IsArray) + + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public static FileParameter ParameterToFile(string name, Stream stream) { - var valueArray = (Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) + if (stream is FileStream fs) { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + return new FileParameter(name, stream, Path.GetFileName(fs.Name)); } - return string.Join(",", valueTextArray); + return new FileParameter(name, stream, "no_file_name_provided"); } - - 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) + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public static FileParameter ParameterToFile(string name, byte[] data) { - return new FileParameter(name, stream, Path.GetFileName(fs.Name)); + return new FileParameter(name, data, "no_file_name_provided"); } - 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) + private static Dictionary> CollectHeaders(HttpResponseMessage response) { - headers[item.Key] = item.Value; - } + 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; - } + foreach (var item in response.Content.Headers) + { + headers[item.Key] = item.Value; + } - return headers; - } + return headers; + } - private StreamContent ToStreamContent(FileParameter fileParameter) - { - var stream = fileParameter.FileData; - var streamContent = new StreamContent(stream); + private StreamContent ToStreamContent(FileParameter fileParameter) + { + var stream = fileParameter.FileData; + var streamContent = new StreamContent(stream); - streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileParameter.ContentType); + streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileParameter.ContentType); - var dispositionHeader = ContentDispositionHeaderValue.Parse($"form-data; name=\"{fileParameter.ParameterName}\"; filename=\"{fileParameter.FileName}\""); - streamContent.Headers.ContentDisposition = dispositionHeader; + var dispositionHeader = ContentDispositionHeaderValue.Parse($"form-data; name=\"{fileParameter.ParameterName}\"; filename=\"{fileParameter.FileName}\""); + streamContent.Headers.ContentDisposition = dispositionHeader; - return streamContent; + return streamContent; + } } -} -public class FileParameter -{ - public FileParameter(string parameterName, Stream fileData, string fileName, string contentType = null) - { - ParameterName = parameterName; - FileName = fileName; - FileData = fileData; - ContentType = contentType; - } + public class FileParameter + { + public FileParameter(string parameterName, Stream fileData, string fileName, string contentType = null) + { + ParameterName = parameterName; + FileName = fileName; + FileData = fileData; + ContentType = contentType; + } - public FileParameter(string parameterName, byte[] fileData, string fileName, string contentType = null) : this(parameterName, new MemoryStream(fileData), fileName, contentType) - { - } + public FileParameter(string parameterName, byte[] fileData, string fileName, string contentType = null) : this(parameterName, new MemoryStream(fileData), fileName, contentType) + { + } - public Stream FileData { get; } - public string FileName { get; } - public string ParameterName { get; } - public string ContentType { get; } + public Stream FileData { get; } + public string FileName { get; } + public string ParameterName { get; } + 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 index ff5b4476a0..587ffde7bd 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache @@ -1,51 +1,54 @@ using System; using System.Collections.Generic; -namespace {{clientPackage}}; +namespace {{clientPackage}} +{ -/// -/// API Exception -/// -public class {{exceptionTypeName}} : Exception { /// - /// Gets or sets the error code (HTTP status code) + /// API Exception /// - /// The error code (HTTP status code). - public int ErrorCode { get; set; } + 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; } + /// + /// Gets or sets the error content (body json object) + /// + /// The error content (Http response body). + public Object ErrorContent { get; private set; } - public IReadOnlyDictionary> Headers{get; private set;} + public IReadOnlyDictionary> Headers{get; private set;} - /// - /// Initializes a new instance of the class. - /// - public {{exceptionTypeName}}() {} + /// + /// 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. + 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; - } + /// + /// 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 index 0d10db5b59..ff73f8877f 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/README.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/README.mustache @@ -17,14 +17,12 @@ This C# SDK is automatically generated by the [Swagger Codegen](https://github.c {{/infoUrl}} -## Frameworks supported -- .NET 2.0 +## Target frameworks supported +- net5.0+ ## Dependencies -- Mono compiler -- Newtonsoft.Json.7.0.1 -- RestSharp.Net2.1.1.11 +- 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 @@ -56,19 +54,9 @@ namespace Example { public void main() { - {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} - // Configure HTTP basic authorization: {{{name}}} - Configuration.Default.Username = "YOUR_USERNAME"; - Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} - // Configure API key authorization: {{{name}}} - Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); - // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} - // Configure OAuth2 access token for authorization: {{{name}}} - Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} - {{/hasAuthMethods}} - - var apiInstance = new {{classname}}(); + {{#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}} @@ -114,35 +102,4 @@ Class | Method | HTTP request | Description {{/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}} -{{#isBasic}}- **Type**: HTTP basic authentication -{{/isBasic}} -{{#isOAuth}}- **Type**: OAuth -- **Flow**: {{flow}} -- **Authorization URL**: {{authorizationUrl}} -- **Scopes**: {{^scopes}}N/A{{/scopes}} -{{#scopes}} - {{scope}}: {{description}} -{{/scopes}} -{{/isOAuth}} - -{{/authMethods}} +{{/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 index 0bd95683b0..98c4b39582 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api.mustache @@ -8,72 +8,73 @@ 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}} +namespace {{package}} { + {{#operations}} /// - /// Initializes a new instance of the class. + /// Represents a collection of functions to interact with the API endpoints /// - /// HttpClient to be used for calls. - /// Base url to be used for calls. - public {{classname}}(HttpClient httpClient, String basePath="{{{basePath}}}") : base(httpClient, basePath) + [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}} } - {{#operation}} - /// - public async {{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + /// + /// Represents a collection of functions to interact with the API endpoints + /// + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public partial class {{classname}} : {{apiClientBaseTypeName}}, I{{classname}} { - {{#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}} + /// + /// 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}} + {{#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}} - } + {{#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}} + {{/operation}} + } + {{/operations}} +} \ No newline at end of file From 59fc802de016ee34f042eb87ac3459fa69102be9 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 11:02:06 +0300 Subject: [PATCH 10/13] minor template refactor --- .../resources/handlebars/csharp-dotnet-core/ApiClient.mustache | 1 - .../handlebars/csharp-dotnet-core/ApiException.mustache | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache index 49b89425de..fb76bbde57 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -14,7 +14,6 @@ using System.Threading.Tasks; namespace {{clientPackage}} { - /// /// Base type for API client is mainly responsible for making the HTTP call to the API. /// diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache index 587ffde7bd..84cf736e87 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache @@ -3,7 +3,6 @@ using System.Collections.Generic; namespace {{clientPackage}} { - /// /// API Exception /// From 14fd5c8b5f891e26491ca194ecd3f38a04a52fdc Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 11:15:28 +0300 Subject: [PATCH 11/13] more templates fun --- .../csharp-dotnet-core/ApiClient.mustache | 63 +++++++++++++------ .../csharp-dotnet-core/ApiException.mustache | 5 +- .../csharp-dotnet-core/api_doc.mustache | 14 +---- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache index fb76bbde57..a8b60a8c59 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiClient.mustache @@ -77,7 +77,7 @@ namespace {{clientPackage}} var status = (int)response.StatusCode; if (status is >= 200 and < 300) { - return await ReadObjectResponseAsync(response, headers, ct).ConfigureAwait(false); + return await DeserializeResponse(response, headers, ct).ConfigureAwait(false); } else { @@ -245,27 +245,12 @@ namespace {{clientPackage}} request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); } - public bool ReadResponseAsString { get; set; } - - protected virtual async Task ReadObjectResponseAsync( + protected virtual async Task DeserializeResponse( HttpResponseMessage response, IReadOnlyDictionary> headers, CancellationToken ct ) { - if (ReadResponseAsString) - { - 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); - } - } try { using (var responseStream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false)) @@ -280,6 +265,24 @@ namespace {{clientPackage}} } } + 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) @@ -409,8 +412,14 @@ namespace {{clientPackage}} } } + /// + /// 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; @@ -419,13 +428,31 @@ namespace {{clientPackage}} 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 index 84cf736e87..b8bec08fc7 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/ApiException.mustache @@ -20,7 +20,10 @@ namespace {{clientPackage}} /// The error content (Http response body). public Object ErrorContent { get; private set; } - public IReadOnlyDictionary> Headers{get; private set;} + /// + /// Headers, used in failed request. + /// + public IReadOnlyDictionary> Headers {get; private set; } /// /// Initializes a new instance of the class. diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache index d8a074e11f..3728ac95b9 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache @@ -32,19 +32,7 @@ namespace Example { public void main() { - {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} - // Configure HTTP basic authorization: {{{name}}} - Configuration.Default.Username = "YOUR_USERNAME"; - Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} - // Configure API key authorization: {{{name}}} - Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); - // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} - // Configure OAuth2 access token for authorization: {{{name}}} - Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} - {{/hasAuthMethods}} - - var apiInstance = new {{classname}}(); + var apiInstance = new {{classname}}(new HttpClient(), "http://my-service.srv"); {{#allParams}} {{#isPrimitiveType}} var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} From c6310613cbfd00af5cac0fe1a9d0b3dda498bba5 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 12:42:30 +0300 Subject: [PATCH 12/13] remove unused doc part --- .../resources/handlebars/csharp-dotnet-core/api_doc.mustache | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache index 3728ac95b9..7d769d750c 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/api_doc.mustache @@ -70,10 +70,6 @@ Name | Type | Description | Notes {{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} -### Authorization - -{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} - ### HTTP request headers - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} From 0c07749efafc1fa33ed965153055f68551758e06 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 23 May 2024 13:42:56 +0300 Subject: [PATCH 13/13] fix namespace in model template --- .../csharp-dotnet-core/model.mustache | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache index 0f580101b9..eea2a87dd8 100644 --- a/src/main/resources/handlebars/csharp-dotnet-core/model.mustache +++ b/src/main/resources/handlebars/csharp-dotnet-core/model.mustache @@ -7,39 +7,40 @@ using System.Text.Json.Serialization; {{#models}} {{#model}} -namespace {{package}}; - -/// -/// {{description}} -/// -[GeneratedCode("swagger-codegen", "{{generatorVersion}}")] -public partial class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} +namespace {{package}} { - {{#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 + /// {{description}} /// - /// String presentation of the object - public override string ToString() + [GeneratedCode("swagger-codegen", "{{generatorVersion}}")] + public partial class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} { - var sb = new StringBuilder(); - sb.Append("class {{classname}} {\n"); - {{#vars}} - sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); - {{/vars}} - sb.Append("}\n"); - return sb.ToString(); + {{#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}} } - - {{/model}} - {{/models}} -} +} \ No newline at end of file