[csharp-netcore] Nrt (nullableReferenceTypes) refactor (#11452)

* refactor nrt annotation

* enable nrt by default in .net6.0+

* use shorter nrt annotation

* build samples

* removed debugging lines
This commit is contained in:
devhl-labs
2022-02-13 22:08:40 -05:00
committed by GitHub
parent c937bae888
commit 140f633655
22 changed files with 98 additions and 87 deletions

View File

@@ -30,7 +30,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |PascalCase|
|netCoreProjectFile|Use the new format (.NET Core) for .NET project files (.csproj).| |false|
|nonPublicApi|Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers.| |false|
|nullableReferenceTypes|Use nullable annotations in the project. Only supported on C# 8 / ASP.NET Core 3.1 or newer.| |false|
|nullableReferenceTypes|Use nullable annotations in the project. Only supported on C# 8 / ASP.NET Core 3.1 or newer. Starting in .NET 6.0 the default is true.| |false|
|optionalAssemblyInfo|Generate AssemblyInfo.cs.| |true|
|optionalEmitDefaultValues|Set DataMember's EmitDefaultValue.| |false|
|optionalMethodArgument|C# Optional method argument, e.g. void square(int x=10) (.net 4.0+ only).| |true|

View File

@@ -369,8 +369,6 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
if (additionalProperties.containsKey(CodegenConstants.NULLABLE_REFERENCE_TYPES)) {
setNullableReferenceTypes(convertPropertyToBooleanAndWriteBack(CodegenConstants.NULLABLE_REFERENCE_TYPES));
} else {
additionalProperties.put(CodegenConstants.NULLABLE_REFERENCE_TYPES, nullReferenceTypesFlag);
}
if (additionalProperties.containsKey(CodegenConstants.INTERFACE_PREFIX)) {
@@ -1165,8 +1163,17 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
public void setNullableReferenceTypes(final Boolean nullReferenceTypesFlag){
this.nullReferenceTypesFlag = nullReferenceTypesFlag;
if (nullReferenceTypesFlag == true){
additionalProperties.put("nullableReferenceTypes", nullReferenceTypesFlag);
additionalProperties.put("nrt", nullReferenceTypesFlag);
if (nullReferenceTypesFlag){
this.nullableType.add("string");
additionalProperties.put("nrt?", "?");
additionalProperties.put("nrt!", "!");
} else {
this.nullableType.remove("string");
additionalProperties.remove("nrt?");
additionalProperties.remove("nrt!");
}
}

View File

@@ -240,7 +240,7 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen {
// CLI Switches
addSwitch(CodegenConstants.NULLABLE_REFERENCE_TYPES,
CodegenConstants.NULLABLE_REFERENCE_TYPES_DESC,
CodegenConstants.NULLABLE_REFERENCE_TYPES_DESC + " Starting in .NET 6.0 the default is true.",
this.nullReferenceTypesFlag);
addSwitch(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
@@ -655,11 +655,17 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen {
if (!netStandard) {
setNetCoreProjectFileFlag(true);
}
if (additionalProperties.containsKey(CodegenConstants.GENERATE_PROPERTY_CHANGED)) {
LOGGER.warn("{} is not supported in the .NET Standard generator.", CodegenConstants.GENERATE_PROPERTY_CHANGED);
additionalProperties.remove(CodegenConstants.GENERATE_PROPERTY_CHANGED);
if (!additionalProperties.containsKey(CodegenConstants.NULLABLE_REFERENCE_TYPES) && !strategies.stream().anyMatch(s ->
s.equals(FrameworkStrategy.NETCOREAPP_2_0) ||
s.equals(FrameworkStrategy.NETCOREAPP_2_1) ||
s.equals(FrameworkStrategy.NETCOREAPP_3_0) ||
s.equals(FrameworkStrategy.NETCOREAPP_3_1) ||
s.equals(FrameworkStrategy.NET_5_0) ||
s.equals(FrameworkStrategy.NETFRAMEWORK_4_7))) {
// starting in .net 6.0, NRT is enabled by default. If not specified, lets enable NRT to match the framework's default
setNullableReferenceTypes(true);
}
}
final AtomicReference<Boolean> excludeTests = new AtomicReference<>();

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
@@ -14,7 +14,7 @@ namespace {{packageName}}.Client
/// <summary>
/// The reason the api request failed
/// </summary>
public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }
public string{{nrt?}} ReasonPhrase { get; }
/// <summary>
/// The HttpStatusCode
@@ -32,7 +32,7 @@ namespace {{packageName}}.Client
/// <param name="reasonPhrase"></param>
/// <param name="statusCode"></param>
/// <param name="rawContent"></param>
public ApiException(string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent)
public ApiException(string{{nrt?}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent)
{
ReasonPhrase = reasonPhrase;

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
@@ -53,7 +53,7 @@ namespace {{packageName}}.Client
/// <param name="parameterName"></param>
public virtual void UseInQuery(System.Net.Http.HttpRequestMessage request, UriBuilder uriBuilder, System.Collections.Specialized.NameValueCollection parseQueryString, string parameterName)
{
parseQueryString[parameterName] = Uri.EscapeDataString(_raw).ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}};
parseQueryString[parameterName] = Uri.EscapeDataString(_raw).ToString(){{nrt!}};
}
}
}

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Collections.Generic;
@@ -42,7 +42,7 @@ namespace {{packageName}}.Client
/// The deserialized content
/// </summary>
{{! .net 3.1 does not support unconstrained nullable T }}
public T{{#nullableReferenceTypes}}{{^netcoreapp3.1}}?{{/netcoreapp3.1}}{{/nullableReferenceTypes}} Content { get; set; }
public T{{#nrt}}{{^netcoreapp3.1}}?{{/netcoreapp3.1}}{{/nrt}} Content { get; set; }
/// <summary>
/// Gets or sets the status code (HTTP status code)
@@ -71,7 +71,7 @@ namespace {{packageName}}.Client
/// <summary>
/// The reason phrase contained in the api response
/// </summary>
public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }
public string{{nrt?}} ReasonPhrase { get; }
/// <summary>
/// The headers contained in the api response

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Linq;

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Linq;

View File

@@ -81,7 +81,7 @@ namespace {{packageName}}.Client
/// <param name="obj">The parameter (header, path, query, form).</param>
/// <param name="format">The DateTime serialization format.</param>
/// <returns>Formatted string.</returns>
public static string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ParameterToString(object obj, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} format = ISO8601_DATETIME_FORMAT)
public static string{{nrt?}} ParameterToString(object obj, string{{nrt?}} format = ISO8601_DATETIME_FORMAT)
{
if (obj is DateTime dateTime)
// Return a formatted date string - Can be customized with Configuration.DateTimeFormat

View File

@@ -35,7 +35,7 @@ namespace {{packageName}}.Client
/// <returns></returns>
public HostConfiguration Add{{apiName}}HttpClients<{{#apiInfo}}{{#apis}}T{{classname}}{{^-last}}, {{/-last}}{{/apis}}>
(
Action<HttpClient>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action<IHttpClientBuilder>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null){{#apis}}
Action<HttpClient>{{nrt?}} client = null, Action<IHttpClientBuilder>{{nrt?}} builder = null){{#apis}}
where T{{classname}} : class, {{interfacePrefix}}{{classname}}{{/apis}}
{
if (client == null)
@@ -61,7 +61,7 @@ namespace {{packageName}}.Client
/// <param name="builder"></param>
/// <returns></returns>
public HostConfiguration Add{{apiName}}HttpClients(
Action<HttpClient>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} client = null, Action<IHttpClientBuilder>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} builder = null)
Action<HttpClient>{{nrt?}} client = null, Action<IHttpClientBuilder>{{nrt?}} builder = null)
{
Add{{apiName}}HttpClients<{{#apiInfo}}{{#apis}}{{classname}}{{^-last}}, {{/-last}}{{/apis}}{{/apiInfo}}>(client, builder);

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Collections.Generic;
@@ -22,7 +22,7 @@ namespace {{packageName}}.Client
/// <summary>
/// Create an instance
/// </summary>
public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPhrase, List<string> httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod)
public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{nrt?}} keyPassPhrase, List<string> httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod)
{
KeyId = keyId;
KeyFilePath = keyFilePath;
@@ -48,7 +48,7 @@ namespace {{packageName}}.Client
/// <summary>
/// Gets the key pass phrase for password protected key
/// </summary>
public SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} KeyPassPhrase { get; set; }
public SecureString{{nrt?}} KeyPassPhrase { get; set; }
/// <summary>
/// Gets the HTTP signing header
@@ -118,7 +118,7 @@ namespace {{packageName}}.Client
//Hash table to store singed headers
var HttpSignedRequestHeader = new Dictionary<string, string>();
var httpSignatureHeader = new Dictionary<string, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>();
var httpSignatureHeader = new Dictionary<string, string{{nrt?}}>();
if (HttpSigningHeader.Count == 0)
HttpSigningHeader.Add("(created)");
@@ -191,7 +191,7 @@ namespace {{packageName}}.Client
//Concatinate headers value separated by new line
var headerValuesString = string.Join("\n", headerValuesList);
var signatureStringHash = GetStringHash(HashAlgorithm.ToString(), headerValuesString);
string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} headerSignatureStr = null;
string{{nrt?}} headerSignatureStr = null;
var keyType = GetKeyType(KeyFilePath);
if (keyType == PrivateKeyType.RSA)
@@ -241,7 +241,7 @@ namespace {{packageName}}.Client
if (KeyPassPhrase == null)
throw new NullReferenceException($"{ nameof(KeyPassPhrase) } was null.");
RSA{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase);
RSA{{nrt?}} rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase);
if (rsa == null)
return string.Empty;
@@ -363,12 +363,12 @@ namespace {{packageName}}.Client
return derBytes.ToArray();
}
private RSACryptoServiceProvider{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} GetRSAProviderFromPemFile(String pemfile, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPharse = null)
private RSACryptoServiceProvider{{nrt?}} GetRSAProviderFromPemFile(String pemfile, SecureString{{nrt?}} keyPassPharse = null)
{
const String pempubheader = "-----BEGIN PUBLIC KEY-----";
const String pempubfooter = "-----END PUBLIC KEY-----";
bool isPrivateKeyFile = true;
byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} pemkey = null;
byte[]{{nrt?}} pemkey = null;
if (!File.Exists(pemfile))
throw new Exception("private key file does not exist.");
@@ -390,7 +390,7 @@ namespace {{packageName}}.Client
return null;
}
private byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ConvertPrivateKeyToBytes(String instr, SecureString{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} keyPassPharse = null)
private byte[]{{nrt?}} ConvertPrivateKeyToBytes(String instr, SecureString{{nrt?}} keyPassPharse = null)
{
const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----";
const String pemprivfooter = "-----END RSA PRIVATE KEY-----";
@@ -415,10 +415,10 @@ namespace {{packageName}}.Client
StringReader str = new StringReader(pvkstr);
//-------- read PEM encryption info. lines and extract salt -----
if (!str.ReadLine(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null?
if (!str.ReadLine(){{nrt!}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null?
return null;
String saltline = str.ReadLine(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}; // TODO: what do we do here if ReadLine is null?
String saltline = str.ReadLine(){{nrt!}}; // TODO: what do we do here if ReadLine is null?
if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,"))
return null;
@@ -443,18 +443,18 @@ namespace {{packageName}}.Client
}
// TODO: what do we do here if keyPassPharse is null?
byte[] deskey = GetEncryptedKey(salt, keyPassPharse{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
byte[] deskey = GetEncryptedKey(salt, keyPassPharse{{nrt!}}, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes
if (deskey == null)
return null;
//------ Decrypt the encrypted 3des-encrypted RSA private key ------
byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
byte[]{{nrt?}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV
return rsakey;
}
}
private RSACryptoServiceProvider{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} DecodeRSAPrivateKey(byte[] privkey)
private RSACryptoServiceProvider{{nrt?}} DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
@@ -583,7 +583,7 @@ namespace {{packageName}}.Client
// ---- do multi-hashing and concatenate results D1, D2 ... into keymaterial bytes ----
MD5 md5 = new MD5CryptoServiceProvider();
byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null;
byte[]{{nrt?}} result = null;
byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget
for (int j = 0; j < miter; j++)
@@ -593,7 +593,7 @@ namespace {{packageName}}.Client
result = data00; //initialize
else
{
Array.Copy(result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, hashtarget, result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.Length); // TODO: what do we do if result is null here?
Array.Copy(result{{nrt!}}, hashtarget, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
Array.Copy(data00, 0, hashtarget, result.Length, data00.Length);
result = hashtarget;
}
@@ -608,13 +608,13 @@ namespace {{packageName}}.Client
Array.Clear(psbytes, 0, psbytes.Length);
Array.Clear(data00, 0, data00.Length);
Array.Clear(result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}, 0, result{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.Length); // TODO: what do we do if result is null here?
Array.Clear(result{{nrt!}}, 0, result{{nrt!}}.Length); // TODO: what do we do if result is null here?
Array.Clear(hashtarget, 0, hashtarget.Length);
Array.Clear(keymaterial, 0, keymaterial.Length);
return deskey;
}
private byte[]{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV)
private byte[]{{nrt?}} DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV)
{
MemoryStream memst = new MemoryStream();
TripleDES alg = TripleDES.Create();

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Linq;

View File

@@ -16,6 +16,6 @@ namespace {{packageName}}.Client
/// An event to track the health of the server.
/// If you store these event args, be sure to purge old event args to prevent a memory leak.
/// </summary>
event ClientUtils.EventHandler<ApiResponseEventArgs>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded;
event ClientUtils.EventHandler<ApiResponseEventArgs>{{nrt?}} ApiResponded;
}
}

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Linq;

View File

@@ -62,7 +62,7 @@ namespace YourProject
{
var host = CreateHostBuilder(args).Build();{{#apiInfo}}{{#apis}}{{#-first}}
var api = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();{{#operations}}{{#-first}}{{#operation}}{{#-first}}
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/-first}}{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> foo = await api.{{operationId}}WithHttpInfoAsync("todo");{{/-first}}{{/operation}}{{/-first}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
}
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args)

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;{{^netStandard}}
using System.Threading.Channels;{{/netStandard}}{{#netStandard}}

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
@@ -18,7 +18,7 @@ namespace {{packageName}}.Client
internal TimeSpan? Timeout { get; set; }
internal delegate void TokenBecameAvailableEventHandler(object sender);
internal event TokenBecameAvailableEventHandler{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} TokenBecameAvailable;
internal event TokenBecameAvailableEventHandler{{nrt?}} TokenBecameAvailable;
/// <summary>

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System.Linq;
using System.Collections.Generic;

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Linq;

View File

@@ -1,6 +1,6 @@
// <auto-generated>
{{>partial_header}}
{{#nullableReferenceTypes}}#nullable enable{{/nullableReferenceTypes}}
{{#nrt}}#nullable enable{{/nrt}}
using System;
using System.Collections.Generic;
@@ -34,8 +34,8 @@ namespace {{packageName}}.{{apiPackage}}
/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns>Task&lt;ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}&gt;&gt;</returns>
Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);
/// <returns>Task&lt;ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}&gt;&gt;</returns>
Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);
/// <summary>
/// {{summary}}
@@ -49,7 +49,7 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns>Task of ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}&gt;</returns>
Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{#nullableReferenceTypes}}
Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{#nrt}}
/// <summary>
/// {{summary}}
@@ -62,7 +62,7 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns>Task of ApiResponse&lt;{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}?&gt;</returns>
Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{/nullableReferenceTypes}}{{^-last}}
Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null);{{/nrt}}{{^-last}}
{{/-last}}{{/operation}}
}
@@ -76,7 +76,7 @@ namespace {{packageName}}.{{apiPackage}}
/// An event to track the health of the server.
/// If you store these event args, be sure to purge old event args to prevent a memory leak.
/// </summary>
public event ClientUtils.EventHandler<ApiResponseEventArgs>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ApiResponded;
public event ClientUtils.EventHandler<ApiResponseEventArgs>{{nrt?}} ApiResponded;
/// <summary>
/// The logger
@@ -143,17 +143,17 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns><see cref="Task"/>&lt;<see cref="{{#returnType}}{{.}}{{/returnType}}{{^returnType}}object{{/returnType}}"/>&gt;</returns>
public async Task<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
public async Task<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> {{operationId}}Async({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
{
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);
{{^nullableReferenceTypes}}{{#returnTypeIsPrimitive}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
{{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}}if (result.Content == null){{^nullableReferenceTypes}}{{#returnTypeIsPrimitive}}
#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}}
{{^nrt}}{{#returnTypeIsPrimitive}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
{{/returnTypeIsPrimitive}}{{/nrt}}if (result.Content == null){{^nrt}}{{#returnTypeIsPrimitive}}
#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/returnTypeIsPrimitive}}{{/nrt}}
throw new ApiException(result.ReasonPhrase, result.StatusCode, result.RawContent);
return result.Content;
}{{#nullableReferenceTypes}}
}{{#nrt}}
/// <summary>
/// {{summary}} {{notes}}
@@ -164,9 +164,9 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns><see cref="Task"/>&lt;<see cref="{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}"/>&gt;</returns>
public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
{
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null;
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}>{{nrt?}} result = null;
try
{
result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);
@@ -178,7 +178,7 @@ namespace {{packageName}}.{{apiPackage}}
return result != null && result.IsSuccessStatusCode
? result.Content
: null;
}{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^returnTypeIsPrimitive}}
}{{/nrt}}{{^nrt}}{{^returnTypeIsPrimitive}}
{{! Note that this method is a copy paste of above due to NRT complexities }}
/// <summary>
/// {{summary}} {{notes}}
@@ -189,9 +189,9 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns><see cref="Task"/>&lt;<see cref="{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}"/>&gt;</returns>
public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
public async Task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> {{operationId}}OrDefaultAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
{
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} result = null;
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}>{{nrt?}} result = null;
try
{
result = await {{operationId}}WithHttpInfoAsync({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}cancellationToken).ConfigureAwait(false);
@@ -204,7 +204,7 @@ namespace {{packageName}}.{{apiPackage}}
? result.Content
: null;
}
{{/returnTypeIsPrimitive}}{{/nullableReferenceTypes}}
{{/returnTypeIsPrimitive}}{{/nrt}}
/// <summary>
/// {{summary}} {{notes}}
@@ -215,24 +215,24 @@ namespace {{packageName}}.{{apiPackage}}
{{/allParams}}
/// <param name="cancellationToken">Cancellation Token to cancel the request.</param>
/// <returns><see cref="Task"/>&lt;<see cref="ApiResponse{T}"/>&gt; where T : <see cref="{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}object{{/returnType}}"/></returns>
public async Task<ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
public async Task<ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}>> {{operationId}}WithHttpInfoAsync({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/required}}{{^required}}{{{dataType}}} {{paramName}} = null{{^-last}}, {{/-last}}{{/required}}{{/allParams}}{{#allParams.0}}, {{/allParams.0}}System.Threading.CancellationToken? cancellationToken = null)
{
try
{
{{#hasRequiredParams}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/hasRequiredParams}}{{#allParams}}{{#required}}{{#nullableReferenceTypes}}
{{#hasRequiredParams}}#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'{{/hasRequiredParams}}{{#allParams}}{{#required}}{{#nrt}}
if ({{paramName}} == null)
throw new ArgumentNullException(nameof({{paramName}}));{{/nullableReferenceTypes}}{{^nullableReferenceTypes}}{{^vendorExtensions.x-csharp-value-type}}
throw new ArgumentNullException(nameof({{paramName}}));{{/nrt}}{{^nrt}}{{^vendorExtensions.x-csharp-value-type}}
if ({{paramName}} == null)
throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nullableReferenceTypes}}{{/required}}{{/allParams}}{{#hasRequiredParams}}
throw new ArgumentNullException(nameof({{paramName}}));{{/vendorExtensions.x-csharp-value-type}}{{/nrt}}{{/required}}{{/allParams}}{{#hasRequiredParams}}
#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null'
{{/hasRequiredParams}}using (HttpRequestMessage request = new HttpRequestMessage())
{
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Host = HttpClient.BaseAddress{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.Host;
uriBuilder.Host = HttpClient.BaseAddress{{nrt!}}.Host;
uriBuilder.Scheme = ClientUtils.SCHEME;
uriBuilder.Path = ClientUtils.CONTEXT_PATH + "{{path}}";{{#pathParams}}{{#required}}
uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString()));{{/required}}{{^required}}
@@ -246,10 +246,10 @@ namespace {{packageName}}.{{apiPackage}}
System.Collections.Specialized.NameValueCollection parseQueryString = System.Web.HttpUtility.ParseQueryString(string.Empty);{{/isKeyInQuery}}{{/isApiKey}}{{/authMethods}}{{/queryParams}}{{#queryParams}}{{#required}}{{#-first}}
{{! all the redundant tags here are to get the spacing just right }}
{{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}});
{{/-first}}{{/required}}{{/queryParams}}{{#queryParams}}{{#required}}parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{nrt!}});
{{/required}}{{/queryParams}}{{#queryParams}}{{#-first}}
{{/-first}}{{/queryParams}}{{#queryParams}}{{^required}}if ({{paramName}} != null)
parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{#nullableReferenceTypes}}!{{/nullableReferenceTypes}});
parseQueryString["{{baseName}}"] = Uri.EscapeDataString({{paramName}}.ToString(){{nrt!}});
{{/required}}{{#-last}}uriBuilder.Query = parseQueryString.ToString();{{/-last}}{{/queryParams}}{{#headerParams}}{{#required}}
@@ -262,14 +262,14 @@ namespace {{packageName}}.{{apiPackage}}
request.Content = multipartContent;
List<KeyValuePair<string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>> formParams = new List<KeyValuePair<string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>>();
List<KeyValuePair<string{{nrt?}}, string{{nrt?}}>> formParams = new List<KeyValuePair<string{{nrt?}}, string{{nrt?}}>>();
multipartContent.Add(new FormUrlEncodedContent(formParams));{{/-first}}{{^isFile}}{{#required}}
formParams.Add(new KeyValuePair<string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}}
formParams.Add(new KeyValuePair<string{{nrt?}}, string{{nrt?}}>("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{^required}}
if ({{paramName}} != null)
formParams.Add(new KeyValuePair<string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}, string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}}
formParams.Add(new KeyValuePair<string{{nrt?}}, string{{nrt?}}>("{{baseName}}", ClientUtils.ParameterToString({{paramName}})));{{/required}}{{/isFile}}{{#isFile}}{{#required}}
multipartContent.Add(new StreamContent({{paramName}}));{{/required}}{{^required}}
@@ -322,7 +322,7 @@ namespace {{packageName}}.{{apiPackage}}
tokens.Add(signatureToken);
string requestBody = await request.Content{{#nullableReferenceTypes}}!{{/nullableReferenceTypes}}.ReadAsStringAsync({{^netStandard}}{{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}{{/netStandard}}).ConfigureAwait(false);
string requestBody = await request.Content{{nrt!}}.ReadAsStringAsync({{^netStandard}}{{^netcoreapp3.1}}cancellationToken.GetValueOrDefault(){{/netcoreapp3.1}}{{/netStandard}}).ConfigureAwait(false);
signatureToken.UseInHeader(request, requestBody, cancellationToken);{{/isHttpSignature}}{{/authMethods}}{{#consumes}}{{#-first}}
@@ -331,7 +331,7 @@ namespace {{packageName}}.{{apiPackage}}
{{/-last}}{{#-last}}
};{{/-last}}{{/consumes}}{{#consumes}}{{#-first}}
string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} contentType = ClientUtils.SelectHeaderContentType(contentTypes);
string{{nrt?}} contentType = ClientUtils.SelectHeaderContentType(contentTypes);
if (contentType != null)
request.Content.Headers.Add("ContentType", contentType);{{/-first}}{{/consumes}}{{#produces}}{{#-first}}
@@ -341,7 +341,7 @@ namespace {{packageName}}.{{apiPackage}}
{{/-last}}{{/produces}}{{#produces}}{{#-last}}
};{{/-last}}{{/produces}}{{#produces}}{{#-first}}
string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} accept = ClientUtils.SelectHeaderAccept(accepts);
string{{nrt?}} accept = ClientUtils.SelectHeaderAccept(accepts);
if (accept != null)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
@@ -367,7 +367,7 @@ namespace {{packageName}}.{{apiPackage}}
}
}
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}}>(responseMessage, responseContent);
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}> apiResponse = new ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}}>(responseMessage, responseContent);
if (apiResponse.IsSuccessStatusCode)
apiResponse.Content = Newtonsoft.Json.JsonConvert.DeserializeObject<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}object{{/returnType}}>(apiResponse.RawContent, ClientUtils.JsonSerializerSettings);{{#authMethods}}

View File

@@ -19,10 +19,8 @@
<RepositoryUrl>https://{{{gitHost}}}/{{{gitUserId}}}/{{{gitRepoId}}}.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>{{#releaseNote}}
<PackageReleaseNotes>{{.}}</PackageReleaseNotes>{{/releaseNote}}{{#packageTags}}
<PackageTags>{{{.}}}</PackageTags>{{/packageTags}}
{{#nullableReferenceTypes}}
<Nullable>annotations</Nullable>
{{/nullableReferenceTypes}}
<PackageTags>{{{.}}}</PackageTags>{{/packageTags}}{{#nrt}}
<Nullable>annotations</Nullable>{{/nrt}}
</PropertyGroup>
<ItemGroup>

View File

@@ -4,8 +4,8 @@
<AssemblyName>{{testPackageName}}</AssemblyName>
<RootNamespace>{{testPackageName}}</RootNamespace>
<TargetFramework{{#multiTarget}}s{{/multiTarget}}>{{testTargetFramework}}</TargetFramework{{#multiTarget}}s{{/multiTarget}}>
<IsPackable>false</IsPackable>{{#nullableReferenceTypes}}
<Nullable>annotations</Nullable>{{/nullableReferenceTypes}}
<IsPackable>false</IsPackable>{{#nrt}}
<Nullable>annotations</Nullable>{{/nrt}}
</PropertyGroup>
<ItemGroup>