From 59337d16b82820d1521fc3616df148864e658511 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Sat, 25 Oct 2025 13:10:34 -0400 Subject: [PATCH] feat(dotnet-sdk): Added support for generating an interface for the client --- Makefile | 4 +- config/clients/dotnet/config.overrides.json | 4 + .../dotnet/template/Client/Client.mustache | 2 +- .../dotnet/template/Client/IClient.mustache | 190 ++++++++++ .../modelWriteRequestDeletes.mustache | 338 ++++++++++++++++++ .../template/modelWriteRequestWrites.mustache | 338 ++++++++++++++++++ 6 files changed, 873 insertions(+), 3 deletions(-) create mode 100644 config/clients/dotnet/template/Client/IClient.mustache create mode 100644 config/clients/dotnet/template/modelWriteRequestDeletes.mustache create mode 100644 config/clients/dotnet/template/modelWriteRequestWrites.mustache diff --git a/Makefile b/Makefile index 2bdb907a..17bb191d 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # Main config OPENFGA_DOCKER_TAG = v1 -OPEN_API_REF ?= e53c69cc55317404d02a6d8e418d626268f28a59 +OPEN_API_REF ?= 0ac19aac54f21f3c78970126b84b4c69c6e3b9a2 OPEN_API_URL = https://raw.githubusercontent.com/openfga/api/${OPEN_API_REF}/docs/openapiv2/apidocs.swagger.json -OPENAPI_GENERATOR_CLI_DOCKER_TAG ?= v6.4.0 +OPENAPI_GENERATOR_CLI_DOCKER_TAG ?= v7.15.0 NODE_DOCKER_TAG = 20-alpine GO_DOCKER_TAG = 1 DOTNET_DOCKER_TAG = 9.0 diff --git a/config/clients/dotnet/config.overrides.json b/config/clients/dotnet/config.overrides.json index 5bb78cb7..7aef7e5f 100644 --- a/config/clients/dotnet/config.overrides.json +++ b/config/clients/dotnet/config.overrides.json @@ -66,6 +66,10 @@ "destinationFilename": "src/OpenFga.Sdk/Client/Client.cs", "templateType": "SupportingFiles" }, + "Client/IClient.mustache": { + "destinationFilename": "src/OpenFga.Sdk/Client/IClient.cs", + "templateType": "SupportingFiles" + }, "Client/ClientConfiguration.mustache": { "destinationFilename": "src/OpenFga.Sdk/Client/ClientConfiguration.cs", "templateType": "SupportingFiles" diff --git a/config/clients/dotnet/template/Client/Client.mustache b/config/clients/dotnet/template/Client/Client.mustache index 483d1632..d47b077e 100644 --- a/config/clients/dotnet/template/Client/Client.mustache +++ b/config/clients/dotnet/template/Client/Client.mustache @@ -19,7 +19,7 @@ using {{packageName}}.Model; namespace {{packageName}}.Client; -public class {{appShortName}}Client : IDisposable { +public class {{appShortName}}Client : I{{appShortName}}Client, IDisposable { private readonly ClientConfiguration _configuration; protected {{appShortName}}Api api; private string CLIENT_BULK_REQUEST_ID_HEADER = "{{clientBulkRequestIdHeader}}"; diff --git a/config/clients/dotnet/template/Client/IClient.mustache b/config/clients/dotnet/template/Client/IClient.mustache new file mode 100644 index 00000000..fee1948f --- /dev/null +++ b/config/clients/dotnet/template/Client/IClient.mustache @@ -0,0 +1,190 @@ +{{>partial_header}} + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +using {{packageName}}.Api; +using {{packageName}}.ApiClient; +using {{packageName}}.Client.Model; +#if NETSTANDARD2_0 || NET48 +using {{packageName}}.Client.Extensions; +#endif +using {{packageName}}.Exceptions; +using {{packageName}}.Model; + +namespace {{packageName}}.Client; + +public interface I{{appShortName}}Client { + /// + /// Store ID + /// + string? StoreId { get; set; } + + /// + /// Authorization Model ID + /// + string? AuthorizationModelId { get; set; } + + /********** + * Stores * + **********/ + + /** + * ListStores - Get a paginated list of stores. + */ + Task ListStores(IClientListStoresRequest? body, IClientListStoresOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * CreateStore - Initialize a store + */ + Task CreateStore(ClientCreateStoreRequest body, + IClientRequestOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * GetStore - Get information about the current store + */ + Task GetStore(IClientRequestOptionsWithStoreId? options = default, CancellationToken cancellationToken = default); + + /** + * DeleteStore - Delete a store + */ + Task DeleteStore(IClientRequestOptionsWithStoreId? options = default, CancellationToken cancellationToken = default); + + /************************ + * Authorization Models * + ************************/ + + /** + * ReadAuthorizationModels - Read all authorization models + */ + Task ReadAuthorizationModels( + IClientReadAuthorizationModelsOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * WriteAuthorizationModel - Create a new version of the authorization model + */ + Task WriteAuthorizationModel(ClientWriteAuthorizationModelRequest body, + IClientRequestOptionsWithStoreId? options = default, + CancellationToken cancellationToken = default); + + /** + * ReadAuthorizationModel - Read the current authorization model + */ + Task ReadAuthorizationModel( + IClientReadAuthorizationModelOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * ReadLatestAuthorizationModel - Read the latest authorization model for the current store + */ + Task ReadLatestAuthorizationModel( + IClientRequestOptionsWithAuthZModelId? options = default, + CancellationToken cancellationToken = default); + + /*********************** + * Relationship Tuples * + ***********************/ + + /** + * Read Changes - Read the list of historical relationship tuple writes and deletes + */ + Task ReadChanges(ClientReadChangesRequest? body = default, + ClientReadChangesOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * Read - Read tuples previously written to the store (does not evaluate) + */ + Task Read(ClientReadRequest? body = default, IClientReadOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * Write - Create or delete relationship tuples + */ + Task Write(ClientWriteRequest body, IClientWriteOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * WriteTuples - Utility method to write tuples, wraps Write + */ + Task WriteTuples(List body, IClientWriteOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * DeleteTuples - Utility method to delete tuples, wraps Write + */ + Task DeleteTuples(List body, IClientWriteOptions? options = default, + CancellationToken cancellationToken = default); + + /************************ + * Relationship Queries * + ************************/ + + /** + * Check - Check if a user has a particular relation with an object (evaluates) + */ + Task Check(IClientCheckRequest body, + IClientCheckOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * BatchCheck - Run a set of checks (evaluates) + */ + Task BatchCheck(List body, + IClientBatchCheckOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * Expand - Expands the relationships in userset tree format (evaluates) + */ + Task Expand(IClientExpandRequest body, + IClientExpandOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * ListObjects - List the objects of a particular type that the user has a certain relation to (evaluates) + */ + Task ListObjects(IClientListObjectsRequest body, + IClientListObjectsOptions? options = default, + CancellationToken cancellationToken = default); + + + /** + * ListRelations - List all the relations a user has with an object (evaluates) + */ + Task ListRelations(IClientListRelationsRequest body, + IClientListRelationsOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * ListUsers - List all users of the given type that the object has a relation with (evaluates) + */ + Task ListUsers(IClientListUsersRequest body, + IClientListUsersOptions? options = default, + CancellationToken cancellationToken = default); + + /************** + * Assertions * + **************/ + + /** + * ReadAssertions - Read assertions for a particular authorization model + */ + Task ReadAssertions(IClientReadAssertionsOptions? options = default, + CancellationToken cancellationToken = default); + + /** + * WriteAssertions - Updates assertions for a particular authorization model + */ + Task WriteAssertions(List body, + IClientWriteAssertionsOptions? options = default, + CancellationToken cancellationToken = default); +} diff --git a/config/clients/dotnet/template/modelWriteRequestDeletes.mustache b/config/clients/dotnet/template/modelWriteRequestDeletes.mustache new file mode 100644 index 00000000..e1ca2de6 --- /dev/null +++ b/config/clients/dotnet/template/modelWriteRequestDeletes.mustache @@ -0,0 +1,338 @@ +{{>partial_header}} + +using System; +using System.Collections.Generic; +using System.Linq; +{{#validatable}} +using System.ComponentModel.DataAnnotations; +{{/validatable}} +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + + +namespace {{packageName}}.{{modelPackage}} +{ +{{#models}} +{{#model}} + /// + /// {{description}}{{^description}}{{classname}}{{/description}} + /// + [DataContract(Name = "{{{name}}}")] + {{#discriminator}} + [JsonConverter(typeof(JsonSubtypes), "{{{discriminatorName}}}")] + {{#mappedModels}} + [JsonSubtypes.KnownSubType(typeof({{{modelName}}}), "{{{mappingName}}}")] + {{/mappedModels}} + {{/discriminator}} + public {{#isEnum}}{{^isArray}}{{>visibility}}{{/isArray}}{{/isEnum}}{{^isEnum}}partial {{/isEnum}}class {{{classname}}}{{#parent}} : {{{.}}}{{/parent}}{{^parent}} : IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}}{{/parent}} + { + /// + /// Defines OnMissing behavior + /// + public enum OnMissingEnum + { + /// + /// Enum Error for value: error + /// + [EnumMember(Value = "error")] + Error = 1, + + /// + /// Enum Ignore for value: ignore + /// + [EnumMember(Value = "ignore")] + Ignore = 2 + } + +{{#vars}} +{{#isEnum}} +{{#allowableValues}} + /// + /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + /// +{{#description}} + /// {{.}} +{{/description}} +{{#isContainer}} + [DataMember(Name="{{baseName}}", EmitDefaultValue={{emitDefaultValue}})] +{{/isContainer}} + public {{#isArray}}{{#uniqueItems}}HashSet{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isArray}}{{#isMap}}Dictionary{{/isMap}}{{^isContainer}}{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{dataType}}}{{/datatypeWithEnum}}{{/isContainer}} {{name}} { get; set; } +{{^isContainer}} +{{>modelInnerEnum}} +{{/isContainer}} +{{/allowableValues}} +{{/isEnum}} +{{/vars}} + + /// + /// Initializes a new instance of the class. + /// + [JsonConstructor] + public {{{classname}}}() + { + {{#vars}} + {{#defaultValue}} + {{^isReadOnly}} + {{^isContainer}} + this.{{name}} = {{{defaultValue}}}; + {{/isContainer}} + {{/isReadOnly}} + {{/defaultValue}} + {{/vars}} + this.AdditionalProperties = new Dictionary(); + } + + /// + /// Initializes a new instance of the class. + /// +{{#vars}} +{{^isReadOnly}} + /// {{#description}}{{.}}{{/description}}{{^description}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{/description}}{{^required}} (default to {{defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}. +{{/isReadOnly}} +{{/vars}} + public {{{classname}}}({{#readWriteVars}}{{{dataType}}} {{#lambda.camelcase_param}}{{{name}}}{{/lambda.camelcase_param}} = default{{^-last}}, {{/-last}}{{/readWriteVars}}) + { +{{#vars}} +{{#required}} + {{^defaultValue}} + {{^isReadOnly}} + {{^isNullable}} + // to ensure "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" is required (not null) + if ({{#lambda.camelcase_param}}{{{name}}}{{/lambda.camelcase_param}} == null) + { + throw new ArgumentNullException("{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} is a required property for {{classname}} and cannot be null"); + } + {{/isNullable}} + {{/isReadOnly}} + {{/defaultValue}} + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; +{{/required}} +{{/vars}} +{{#vars}} +{{^required}} + {{#defaultValue}} + {{^isReadOnly}} + // use default value if no "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" provided + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} ?? {{{defaultValue}}}; + {{/isReadOnly}} + {{/defaultValue}} + {{^defaultValue}} + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; + {{/defaultValue}} +{{/required}} +{{/vars}} + this.AdditionalProperties = new Dictionary(); + } + +{{#vars}} + /// + /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + /// {{#description}} + /// {{.}}{{/description}} +{{^isEnum}} + [DataMember(Name = "{{baseName}}", {{#required}}IsRequired = true, {{/required}}EmitDefaultValue = {{emitDefaultValue}})] +{{/isEnum}} +{{#deprecated}} + [Obsolete] +{{/deprecated}} +{{#minimum}} +{{#maximum}} + [Range({{minimum}}, {{maximum}})] +{{/maximum}} +{{/minimum}} +{{#minLength}} +{{#maxLength}} + [StringLength({{maxLength}}, MinimumLength={{minLength}})] +{{/maxLength}} +{{^maxLength}} + [MinLength({{.}})] +{{/maxLength}} +{{/minLength}} +{{^minLength}} +{{#maxLength}} + [MaxLength({{.}})] +{{/maxLength}} +{{/minLength}} +{{#pattern}} + [RegularExpression("{{.}}")] +{{/pattern}} + [JsonPropertyName("{{baseName}}")] +{{#isNullable}} + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +{{/isNullable}} + public {{{dataType}}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; } + +{{/vars}} + /// + /// Gets or Sets additional properties + /// + [JsonExtensionData] + public IDictionary AdditionalProperties { get; set; } + +{{#vendorExtensions.x-serialization-methods}} + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return JsonSerializer.Serialize(this); + } + + /// + /// Builds a {{classname}} from the JSON string presentation of the object + /// + /// {{classname}} + public static {{classname}} FromJson(string jsonString) { + return JsonSerializer.Deserialize<{{classname}}>(jsonString) ?? throw new InvalidOperationException(); + } + +{{/vendorExtensions.x-serialization-methods}} +{{^isEnum}} +{{^parent}} + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + return this.Equals(input as {{classname}}); + {{/useCompareNetObjects}} + } + + /// + /// Returns true if {{classname}} instances are equal + /// + /// Instance of {{classname}} to be compared + /// Boolean + public bool Equals({{classname}} input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + if (input == null) + { + return false; + } + return {{#vars}}{{#parent}}base.Equals(input) && {{/parent}}{{^isContainer}} + ( + this.{{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}} + {{#required}} + {{^isNullable}} + this.{{name}} != null && + {{/isNullable}} + {{/required}} + {{^required}} + (this.{{name}} != null && + {{/required}} + {{/vendorExtensions.x-is-value-type}} + this.{{name}}.Equals(input.{{name}}){{^vendorExtensions.x-is-value-type}}){{/vendorExtensions.x-is-value-type}} + ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}} + ( + this.{{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}} + this.{{name}} != null && + input.{{name}} != null && + {{/vendorExtensions.x-is-value-type}} + this.{{name}}.SequenceEqual(input.{{name}}) + ){{^-last}} && {{/-last}}{{/isContainer}}{{/vars}} + && (this.AdditionalProperties.Count == input.AdditionalProperties.Count && this.AdditionalProperties.All(kv => input.AdditionalProperties.ContainsKey(kv.Key) && Equals(kv.Value, input.AdditionalProperties[kv.Key]))); + {{/useCompareNetObjects}} + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = {{hashCodeBasePrimeNumber}}; +{{#vars}} + if (this.{{name}} != null) + { + hashCode = (hashCode * {{hashCodeMultiplierPrimeNumber}}) + this.{{name}}.GetHashCode(); + } +{{/vars}} + if (this.AdditionalProperties != null) + { + hashCode = (hashCode * {{hashCodeMultiplierPrimeNumber}}) + this.AdditionalProperties.GetHashCode(); + } + return hashCode; + } + } + +{{/parent}} +{{/isEnum}} +{{#validatable}} +{{^parent}} + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { +{{#vars}} +{{#hasValidation}} +{{#maxLength}} + // {{{name}}} ({{{dataType}}}) maxLength + if (this.{{{name}}} != null && this.{{{name}}}.Length > {{maxLength}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, length must be less than {{maxLength}}.", new [] { "{{{name}}}" }); + } + +{{/maxLength}} +{{#minLength}} + // {{{name}}} ({{{dataType}}}) minLength + if (this.{{{name}}} != null && this.{{{name}}}.Length < {{minLength}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, length must be greater than {{minLength}}.", new [] { "{{{name}}}" }); + } + +{{/minLength}} +{{#maximum}} + // {{{name}}} ({{{dataType}}}) maximum + if (this.{{{name}}} > ({{{dataType}}}){{maximum}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must be a value less than or equal to {{maximum}}.", new [] { "{{{name}}}" }); + } + +{{/maximum}} +{{#minimum}} + // {{{name}}} ({{{dataType}}}) minimum + if (this.{{{name}}} < ({{{dataType}}}){{minimum}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must be a value greater than or equal to {{minimum}}.", new [] { "{{{name}}}" }); + } + +{{/minimum}} +{{#pattern}} + // {{{name}}} ({{{dataType}}}) pattern + Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}); + if (false == regex{{{name}}}.Match(this.{{{name}}}).Success) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" }); + } + +{{/pattern}} +{{/hasValidation}} +{{/vars}} + yield break; + } +{{/parent}} +{{/validatable}} + } +{{/model}} +{{/models}} +} diff --git a/config/clients/dotnet/template/modelWriteRequestWrites.mustache b/config/clients/dotnet/template/modelWriteRequestWrites.mustache new file mode 100644 index 00000000..5bc5d657 --- /dev/null +++ b/config/clients/dotnet/template/modelWriteRequestWrites.mustache @@ -0,0 +1,338 @@ +{{>partial_header}} + +using System; +using System.Collections.Generic; +using System.Linq; +{{#validatable}} +using System.ComponentModel.DataAnnotations; +{{/validatable}} +using System.Runtime.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; + + +namespace {{packageName}}.{{modelPackage}} +{ +{{#models}} +{{#model}} + /// + /// {{description}}{{^description}}{{classname}}{{/description}} + /// + [DataContract(Name = "{{{name}}}")] + {{#discriminator}} + [JsonConverter(typeof(JsonSubtypes), "{{{discriminatorName}}}")] + {{#mappedModels}} + [JsonSubtypes.KnownSubType(typeof({{{modelName}}}), "{{{mappingName}}}")] + {{/mappedModels}} + {{/discriminator}} + public {{#isEnum}}{{^isArray}}{{>visibility}}{{/isArray}}{{/isEnum}}{{^isEnum}}partial {{/isEnum}}class {{{classname}}}{{#parent}} : {{{.}}}{{/parent}}{{^parent}} : IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}}{{/parent}} + { + /// + /// Defines OnDuplicate behavior + /// + public enum OnDuplicateEnum + { + /// + /// Enum Error for value: error + /// + [EnumMember(Value = "error")] + Error = 1, + + /// + /// Enum Ignore for value: ignore + /// + [EnumMember(Value = "ignore")] + Ignore = 2 + } + +{{#vars}} +{{#isEnum}} +{{#allowableValues}} + /// + /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + /// +{{#description}} + /// {{.}} +{{/description}} +{{#isContainer}} + [DataMember(Name="{{baseName}}", EmitDefaultValue={{emitDefaultValue}})] +{{/isContainer}} + public {{#isArray}}{{#uniqueItems}}HashSet{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isArray}}{{#isMap}}Dictionary{{/isMap}}{{^isContainer}}{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{dataType}}}{{/datatypeWithEnum}}{{/isContainer}} {{name}} { get; set; } +{{^isContainer}} +{{>modelInnerEnum}} +{{/isContainer}} +{{/allowableValues}} +{{/isEnum}} +{{/vars}} + + /// + /// Initializes a new instance of the class. + /// + [JsonConstructor] + public {{{classname}}}() + { + {{#vars}} + {{#defaultValue}} + {{^isReadOnly}} + {{^isContainer}} + this.{{name}} = {{{defaultValue}}}; + {{/isContainer}} + {{/isReadOnly}} + {{/defaultValue}} + {{/vars}} + this.AdditionalProperties = new Dictionary(); + } + + /// + /// Initializes a new instance of the class. + /// +{{#vars}} +{{^isReadOnly}} + /// {{#description}}{{.}}{{/description}}{{^description}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{/description}}{{^required}} (default to {{defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}. +{{/isReadOnly}} +{{/vars}} + public {{{classname}}}({{#readWriteVars}}{{{dataType}}} {{#lambda.camelcase_param}}{{{name}}}{{/lambda.camelcase_param}} = default{{^-last}}, {{/-last}}{{/readWriteVars}}) + { +{{#vars}} +{{#required}} + {{^defaultValue}} + {{^isReadOnly}} + {{^isNullable}} + // to ensure "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" is required (not null) + if ({{#lambda.camelcase_param}}{{{name}}}{{/lambda.camelcase_param}} == null) + { + throw new ArgumentNullException("{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} is a required property for {{classname}} and cannot be null"); + } + {{/isNullable}} + {{/isReadOnly}} + {{/defaultValue}} + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; +{{/required}} +{{/vars}} +{{#vars}} +{{^required}} + {{#defaultValue}} + {{^isReadOnly}} + // use default value if no "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" provided + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} ?? {{{defaultValue}}}; + {{/isReadOnly}} + {{/defaultValue}} + {{^defaultValue}} + this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; + {{/defaultValue}} +{{/required}} +{{/vars}} + this.AdditionalProperties = new Dictionary(); + } + +{{#vars}} + /// + /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} + /// {{#description}} + /// {{.}}{{/description}} +{{^isEnum}} + [DataMember(Name = "{{baseName}}", {{#required}}IsRequired = true, {{/required}}EmitDefaultValue = {{emitDefaultValue}})] +{{/isEnum}} +{{#deprecated}} + [Obsolete] +{{/deprecated}} +{{#minimum}} +{{#maximum}} + [Range({{minimum}}, {{maximum}})] +{{/maximum}} +{{/minimum}} +{{#minLength}} +{{#maxLength}} + [StringLength({{maxLength}}, MinimumLength={{minLength}})] +{{/maxLength}} +{{^maxLength}} + [MinLength({{.}})] +{{/maxLength}} +{{/minLength}} +{{^minLength}} +{{#maxLength}} + [MaxLength({{.}})] +{{/maxLength}} +{{/minLength}} +{{#pattern}} + [RegularExpression("{{.}}")] +{{/pattern}} + [JsonPropertyName("{{baseName}}")] +{{#isNullable}} + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +{{/isNullable}} + public {{{dataType}}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; } + +{{/vars}} + /// + /// Gets or Sets additional properties + /// + [JsonExtensionData] + public IDictionary AdditionalProperties { get; set; } + +{{#vendorExtensions.x-serialization-methods}} + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return JsonSerializer.Serialize(this); + } + + /// + /// Builds a {{classname}} from the JSON string presentation of the object + /// + /// {{classname}} + public static {{classname}} FromJson(string jsonString) { + return JsonSerializer.Deserialize<{{classname}}>(jsonString) ?? throw new InvalidOperationException(); + } + +{{/vendorExtensions.x-serialization-methods}} +{{^isEnum}} +{{^parent}} + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + return this.Equals(input as {{classname}}); + {{/useCompareNetObjects}} + } + + /// + /// Returns true if {{classname}} instances are equal + /// + /// Instance of {{classname}} to be compared + /// Boolean + public bool Equals({{classname}} input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + if (input == null) + { + return false; + } + return {{#vars}}{{#parent}}base.Equals(input) && {{/parent}}{{^isContainer}} + ( + this.{{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}} + {{#required}} + {{^isNullable}} + this.{{name}} != null && + {{/isNullable}} + {{/required}} + {{^required}} + (this.{{name}} != null && + {{/required}} + {{/vendorExtensions.x-is-value-type}} + this.{{name}}.Equals(input.{{name}}){{^vendorExtensions.x-is-value-type}}){{/vendorExtensions.x-is-value-type}} + ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}} + ( + this.{{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}} + this.{{name}} != null && + input.{{name}} != null && + {{/vendorExtensions.x-is-value-type}} + this.{{name}}.SequenceEqual(input.{{name}}) + ){{^-last}} && {{/-last}}{{/isContainer}}{{/vars}} + && (this.AdditionalProperties.Count == input.AdditionalProperties.Count && this.AdditionalProperties.All(kv => input.AdditionalProperties.ContainsKey(kv.Key) && Equals(kv.Value, input.AdditionalProperties[kv.Key]))); + {{/useCompareNetObjects}} + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = {{hashCodeBasePrimeNumber}}; +{{#vars}} + if (this.{{name}} != null) + { + hashCode = (hashCode * {{hashCodeMultiplierPrimeNumber}}) + this.{{name}}.GetHashCode(); + } +{{/vars}} + if (this.AdditionalProperties != null) + { + hashCode = (hashCode * {{hashCodeMultiplierPrimeNumber}}) + this.AdditionalProperties.GetHashCode(); + } + return hashCode; + } + } + +{{/parent}} +{{/isEnum}} +{{#validatable}} +{{^parent}} + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { +{{#vars}} +{{#hasValidation}} +{{#maxLength}} + // {{{name}}} ({{{dataType}}}) maxLength + if (this.{{{name}}} != null && this.{{{name}}}.Length > {{maxLength}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, length must be less than {{maxLength}}.", new [] { "{{{name}}}" }); + } + +{{/maxLength}} +{{#minLength}} + // {{{name}}} ({{{dataType}}}) minLength + if (this.{{{name}}} != null && this.{{{name}}}.Length < {{minLength}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, length must be greater than {{minLength}}.", new [] { "{{{name}}}" }); + } + +{{/minLength}} +{{#maximum}} + // {{{name}}} ({{{dataType}}}) maximum + if (this.{{{name}}} > ({{{dataType}}}){{maximum}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must be a value less than or equal to {{maximum}}.", new [] { "{{{name}}}" }); + } + +{{/maximum}} +{{#minimum}} + // {{{name}}} ({{{dataType}}}) minimum + if (this.{{{name}}} < ({{{dataType}}}){{minimum}}) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must be a value greater than or equal to {{minimum}}.", new [] { "{{{name}}}" }); + } + +{{/minimum}} +{{#pattern}} + // {{{name}}} ({{{dataType}}}) pattern + Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}); + if (false == regex{{{name}}}.Match(this.{{{name}}}).Success) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" }); + } + +{{/pattern}} +{{/hasValidation}} +{{/vars}} + yield break; + } +{{/parent}} +{{/validatable}} + } +{{/model}} +{{/models}} +}