From e35da846fbd4896d2666b81dac4dd3fbfa26047a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire?= Date: Fri, 21 Jun 2024 16:19:29 -0400 Subject: [PATCH] openapi: support validation attributes (#1477) * openapi: support validation attributes * wip * Improve NSwag tests * Fix some warnings * Address some comments * more stuff * Discard unrelated changes * Discard more unrealted changes * Add test for length attribute on collection * Address PR comments * stuff * More stuff * Fix .NET 8 things * Fix coding style * Set ConvertValueInInvariantCulture * Add Kiota tests * Address some PR comments * Add missing properties in happy test * Fix CS * Convert DateTime to UTC server-side * Seal classes * Apply suggestions from code review Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Address PR comments * Update test/OpenApiTests/ModelStateValidation/ModelStateValidationTests.cs Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> * Address comments * Add back exclusive range * Address comment --------- Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> --- package-versions.props | 2 +- .../ModelStateValidationClient.cs | 48 + .../GeneratedCode/Models/DataInResponse.cs | 88 ++ .../GeneratedCode/Models/ErrorLinks.cs | 83 ++ .../GeneratedCode/Models/ErrorObject.cs | 179 ++++ .../GeneratedCode/Models/ErrorObject_meta.cs | 58 ++ .../Models/ErrorResponseDocument.cs | 102 +++ .../Models/ErrorResponseDocument_meta.cs | 58 ++ .../GeneratedCode/Models/ErrorSource.cs | 99 ++ .../Models/ErrorTopLevelLinks.cs | 83 ++ .../GeneratedCode/Models/ResourceLinks.cs | 67 ++ .../Models/ResourceTopLevelLinks.cs | 83 ++ ...ialMediaAccountAttributesInPatchRequest.cs | 295 ++++++ ...cialMediaAccountAttributesInPostRequest.cs | 295 ++++++ .../SocialMediaAccountAttributesInResponse.cs | 295 ++++++ .../SocialMediaAccountDataInPatchRequest.cs | 90 ++ .../SocialMediaAccountDataInPostRequest.cs | 74 ++ .../SocialMediaAccountDataInResponse.cs | 90 ++ .../SocialMediaAccountDataInResponse_meta.cs | 58 ++ .../SocialMediaAccountPatchRequestDocument.cs | 67 ++ .../SocialMediaAccountPostRequestDocument.cs | 67 ++ ...cialMediaAccountPrimaryResponseDocument.cs | 115 +++ ...ediaAccountPrimaryResponseDocument_meta.cs | 58 ++ .../Models/SocialMediaAccountResourceType.cs | 14 + .../Models/SocialMediaAccounts.cs | 90 ++ .../Models/SocialMediaAccounts_meta.cs | 58 ++ .../SocialMediaAccountsItemRequestBuilder.cs | 112 +++ .../SocialMediaAccountsRequestBuilder.cs | 127 +++ .../ModelStateValidationTests.cs | 592 ++++++++++++ .../UtcDateTimeJsonConverter.cs | 18 + .../OpenApiKiotaEndToEndTests.csproj | 2 + .../ModelStateValidationTests.cs | 579 ++++++++++++ .../UtcDateTimeJsonConverter.cs | 18 + .../OpenApiNSwagEndToEndTests.csproj | 7 + .../GeneratedSwagger/net6.0/swagger.g.json | 846 +++++++++++++++++ .../GeneratedSwagger/net8.0/swagger.g.json | 864 ++++++++++++++++++ .../ModelStateValidationDbContext.cs | 11 + .../ModelStateValidationFakers.cs | 38 + .../ModelStateValidationTests.cs | 307 +++++++ .../SocialMediaAccount.cs | 96 ++ .../JsonElementAssertionExtensions.cs | 5 + 41 files changed, 6237 insertions(+), 1 deletion(-) create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/ModelStateValidationClient.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/DataInResponse.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorLinks.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject_meta.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument_meta.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorSource.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorTopLevelLinks.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceLinks.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceTopLevelLinks.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPatchRequest.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPostRequest.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInResponse.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPatchRequest.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPostRequest.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse_meta.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPatchRequestDocument.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPostRequestDocument.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument_meta.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountResourceType.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts_meta.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/Item/SocialMediaAccountsItemRequestBuilder.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/SocialMediaAccountsRequestBuilder.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/ModelStateValidationTests.cs create mode 100644 test/OpenApiKiotaEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs create mode 100644 test/OpenApiNSwagEndToEndTests/ModelStateValidation/ModelStateValidationTests.cs create mode 100644 test/OpenApiNSwagEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs create mode 100644 test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net6.0/swagger.g.json create mode 100644 test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net8.0/swagger.g.json create mode 100644 test/OpenApiTests/ModelStateValidation/ModelStateValidationDbContext.cs create mode 100644 test/OpenApiTests/ModelStateValidation/ModelStateValidationFakers.cs create mode 100644 test/OpenApiTests/ModelStateValidation/ModelStateValidationTests.cs create mode 100644 test/OpenApiTests/ModelStateValidation/SocialMediaAccount.cs diff --git a/package-versions.props b/package-versions.props index 5edd2d7ab3..41238fcf3e 100644 --- a/package-versions.props +++ b/package-versions.props @@ -4,7 +4,7 @@ 4.1.0 0.4.1 2.14.1 - 6.6.1 + 6.6.2 13.0.3 diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/ModelStateValidationClient.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/ModelStateValidationClient.cs new file mode 100644 index 0000000000..bf9e33ae22 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/ModelStateValidationClient.cs @@ -0,0 +1,48 @@ +// +using Microsoft.Kiota.Abstractions.Extensions; +using Microsoft.Kiota.Abstractions.Store; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Serialization.Form; +using Microsoft.Kiota.Serialization.Json; +using Microsoft.Kiota.Serialization.Multipart; +using Microsoft.Kiota.Serialization.Text; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.SocialMediaAccounts; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode { + /// + /// The main entry point of the SDK, exposes the configuration and the fluent API. + /// + public class ModelStateValidationClient : BaseRequestBuilder + { + /// The socialMediaAccounts property + public SocialMediaAccountsRequestBuilder SocialMediaAccounts + { + get => new SocialMediaAccountsRequestBuilder(PathParameters, RequestAdapter); + } + /// + /// Instantiates a new and sets the default values. + /// + /// The backing store to use for the models. + /// The request adapter to use to execute the requests. + public ModelStateValidationClient(IRequestAdapter requestAdapter, IBackingStoreFactory backingStore = default) : base(requestAdapter, "{+baseurl}", new Dictionary()) + { + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultSerializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + ApiClientBuilder.RegisterDefaultDeserializer(); + if (string.IsNullOrEmpty(RequestAdapter.BaseUrl)) + { + RequestAdapter.BaseUrl = "http://localhost"; + } + PathParameters.TryAdd("baseurl", RequestAdapter.BaseUrl); + RequestAdapter.EnableBackingStore(backingStore); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/DataInResponse.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/DataInResponse.cs new file mode 100644 index 0000000000..932e4dc2c8 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/DataInResponse.cs @@ -0,0 +1,88 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class DataInResponse : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The id property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#nullable restore +#else + public string Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#endif + /// The type property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } +#nullable restore +#else + public string Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public DataInResponse() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static DataInResponse CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + var mappingValue = parseNode.GetChildNode("type")?.GetStringValue(); + return mappingValue switch + { + "socialMediaAccounts" => new SocialMediaAccountDataInResponse(), + _ => new DataInResponse(), + }; + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"id", n => { Id = n.GetStringValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("id", Id); + writer.WriteStringValue("type", Type); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorLinks.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorLinks.cs new file mode 100644 index 0000000000..3a332296fe --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorLinks.cs @@ -0,0 +1,83 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorLinks : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The about property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? About { + get { return BackingStore?.Get("about"); } + set { BackingStore?.Set("about", value); } + } +#nullable restore +#else + public string About { + get { return BackingStore?.Get("about"); } + set { BackingStore?.Set("about", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The type property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } +#nullable restore +#else + public string Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ErrorLinks() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorLinks CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorLinks(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"about", n => { About = n.GetStringValue(); } }, + {"type", n => { Type = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("about", About); + writer.WriteStringValue("type", Type); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject.cs new file mode 100644 index 0000000000..16e5269395 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject.cs @@ -0,0 +1,179 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorObject : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The code property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Code { + get { return BackingStore?.Get("code"); } + set { BackingStore?.Set("code", value); } + } +#nullable restore +#else + public string Code { + get { return BackingStore?.Get("code"); } + set { BackingStore?.Set("code", value); } + } +#endif + /// The detail property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Detail { + get { return BackingStore?.Get("detail"); } + set { BackingStore?.Set("detail", value); } + } +#nullable restore +#else + public string Detail { + get { return BackingStore?.Get("detail"); } + set { BackingStore?.Set("detail", value); } + } +#endif + /// The id property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#nullable restore +#else + public string Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#endif + /// The links property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ErrorLinks? Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#nullable restore +#else + public ErrorLinks Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#endif + /// The meta property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ErrorObject_meta? Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#nullable restore +#else + public ErrorObject_meta Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#endif + /// The source property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ErrorSource? Source { + get { return BackingStore?.Get("source"); } + set { BackingStore?.Set("source", value); } + } +#nullable restore +#else + public ErrorSource Source { + get { return BackingStore?.Get("source"); } + set { BackingStore?.Set("source", value); } + } +#endif + /// The status property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Status { + get { return BackingStore?.Get("status"); } + set { BackingStore?.Set("status", value); } + } +#nullable restore +#else + public string Status { + get { return BackingStore?.Get("status"); } + set { BackingStore?.Set("status", value); } + } +#endif + /// The title property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Title { + get { return BackingStore?.Get("title"); } + set { BackingStore?.Set("title", value); } + } +#nullable restore +#else + public string Title { + get { return BackingStore?.Get("title"); } + set { BackingStore?.Set("title", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ErrorObject() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorObject CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorObject(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"code", n => { Code = n.GetStringValue(); } }, + {"detail", n => { Detail = n.GetStringValue(); } }, + {"id", n => { Id = n.GetStringValue(); } }, + {"links", n => { Links = n.GetObjectValue(ErrorLinks.CreateFromDiscriminatorValue); } }, + {"meta", n => { Meta = n.GetObjectValue(ErrorObject_meta.CreateFromDiscriminatorValue); } }, + {"source", n => { Source = n.GetObjectValue(ErrorSource.CreateFromDiscriminatorValue); } }, + {"status", n => { Status = n.GetStringValue(); } }, + {"title", n => { Title = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("code", Code); + writer.WriteStringValue("detail", Detail); + writer.WriteStringValue("id", Id); + writer.WriteObjectValue("links", Links); + writer.WriteObjectValue("meta", Meta); + writer.WriteObjectValue("source", Source); + writer.WriteStringValue("status", Status); + writer.WriteStringValue("title", Title); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject_meta.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject_meta.cs new file mode 100644 index 0000000000..6edceb8215 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorObject_meta.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorObject_meta : IAdditionalDataHolder, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { + get { return BackingStore.Get>("AdditionalData") ?? throw new InvalidOperationException("AdditionalData can not be null"); } + set { BackingStore.Set("AdditionalData", value); } + } + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// + /// Instantiates a new and sets the default values. + /// + public ErrorObject_meta() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorObject_meta CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorObject_meta(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument.cs new file mode 100644 index 0000000000..dfba3e4512 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument.cs @@ -0,0 +1,102 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorResponseDocument : ApiException, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The errors property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Errors { + get { return BackingStore?.Get?>("errors"); } + set { BackingStore?.Set("errors", value); } + } +#nullable restore +#else + public List Errors { + get { return BackingStore?.Get>("errors"); } + set { BackingStore?.Set("errors", value); } + } +#endif + /// The links property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ErrorTopLevelLinks? Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#nullable restore +#else + public ErrorTopLevelLinks Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#endif + /// The primary error message. + public override string Message { get => base.Message; } + /// The meta property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ErrorResponseDocument_meta? Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#nullable restore +#else + public ErrorResponseDocument_meta Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ErrorResponseDocument() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorResponseDocument CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorResponseDocument(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"errors", n => { Errors = n.GetCollectionOfObjectValues(ErrorObject.CreateFromDiscriminatorValue)?.ToList(); } }, + {"links", n => { Links = n.GetObjectValue(ErrorTopLevelLinks.CreateFromDiscriminatorValue); } }, + {"meta", n => { Meta = n.GetObjectValue(ErrorResponseDocument_meta.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteCollectionOfObjectValues("errors", Errors); + writer.WriteObjectValue("links", Links); + writer.WriteObjectValue("meta", Meta); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument_meta.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument_meta.cs new file mode 100644 index 0000000000..6c77bba3df --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorResponseDocument_meta.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorResponseDocument_meta : IAdditionalDataHolder, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { + get { return BackingStore.Get>("AdditionalData") ?? throw new InvalidOperationException("AdditionalData can not be null"); } + set { BackingStore.Set("AdditionalData", value); } + } + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// + /// Instantiates a new and sets the default values. + /// + public ErrorResponseDocument_meta() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorResponseDocument_meta CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorResponseDocument_meta(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorSource.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorSource.cs new file mode 100644 index 0000000000..8a4df7fed9 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorSource.cs @@ -0,0 +1,99 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorSource : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The header property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Header { + get { return BackingStore?.Get("header"); } + set { BackingStore?.Set("header", value); } + } +#nullable restore +#else + public string Header { + get { return BackingStore?.Get("header"); } + set { BackingStore?.Set("header", value); } + } +#endif + /// The parameter property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Parameter { + get { return BackingStore?.Get("parameter"); } + set { BackingStore?.Set("parameter", value); } + } +#nullable restore +#else + public string Parameter { + get { return BackingStore?.Get("parameter"); } + set { BackingStore?.Set("parameter", value); } + } +#endif + /// The pointer property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Pointer { + get { return BackingStore?.Get("pointer"); } + set { BackingStore?.Set("pointer", value); } + } +#nullable restore +#else + public string Pointer { + get { return BackingStore?.Get("pointer"); } + set { BackingStore?.Set("pointer", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ErrorSource() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorSource CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorSource(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"header", n => { Header = n.GetStringValue(); } }, + {"parameter", n => { Parameter = n.GetStringValue(); } }, + {"pointer", n => { Pointer = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("header", Header); + writer.WriteStringValue("parameter", Parameter); + writer.WriteStringValue("pointer", Pointer); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorTopLevelLinks.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorTopLevelLinks.cs new file mode 100644 index 0000000000..15ce3b1b93 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ErrorTopLevelLinks.cs @@ -0,0 +1,83 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ErrorTopLevelLinks : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The describedby property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Describedby { + get { return BackingStore?.Get("describedby"); } + set { BackingStore?.Set("describedby", value); } + } +#nullable restore +#else + public string Describedby { + get { return BackingStore?.Get("describedby"); } + set { BackingStore?.Set("describedby", value); } + } +#endif + /// The self property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#nullable restore +#else + public string Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ErrorTopLevelLinks() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ErrorTopLevelLinks CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ErrorTopLevelLinks(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"describedby", n => { Describedby = n.GetStringValue(); } }, + {"self", n => { Self = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("describedby", Describedby); + writer.WriteStringValue("self", Self); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceLinks.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceLinks.cs new file mode 100644 index 0000000000..c9a5c80608 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceLinks.cs @@ -0,0 +1,67 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ResourceLinks : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The self property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#nullable restore +#else + public string Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ResourceLinks() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ResourceLinks CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ResourceLinks(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"self", n => { Self = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("self", Self); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceTopLevelLinks.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceTopLevelLinks.cs new file mode 100644 index 0000000000..b13259fe7c --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/ResourceTopLevelLinks.cs @@ -0,0 +1,83 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class ResourceTopLevelLinks : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The describedby property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Describedby { + get { return BackingStore?.Get("describedby"); } + set { BackingStore?.Set("describedby", value); } + } +#nullable restore +#else + public string Describedby { + get { return BackingStore?.Get("describedby"); } + set { BackingStore?.Set("describedby", value); } + } +#endif + /// The self property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#nullable restore +#else + public string Self { + get { return BackingStore?.Get("self"); } + set { BackingStore?.Set("self", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public ResourceTopLevelLinks() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static ResourceTopLevelLinks CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new ResourceTopLevelLinks(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"describedby", n => { Describedby = n.GetStringValue(); } }, + {"self", n => { Self = n.GetStringValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteStringValue("describedby", Describedby); + writer.WriteStringValue("self", Self); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPatchRequest.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPatchRequest.cs new file mode 100644 index 0000000000..75688fb5f1 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPatchRequest.cs @@ -0,0 +1,295 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountAttributesInPatchRequest : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The age property + public double? Age { + get { return BackingStore?.Get("age"); } + set { BackingStore?.Set("age", value); } + } + /// The alternativeId property + public Guid? AlternativeId { + get { return BackingStore?.Get("alternativeId"); } + set { BackingStore?.Set("alternativeId", value); } + } + /// The backgroundPicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#nullable restore +#else + public string BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The countryCode property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#nullable restore +#else + public string CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#endif + /// The creditCard property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#nullable restore +#else + public string CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#endif + /// The email property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#nullable restore +#else + public string Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#endif + /// The firstName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#nullable restore +#else + public string FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#endif + /// The lastName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#nullable restore +#else + public string LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#endif + /// The nextRevalidation property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#nullable restore +#else + public string NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#endif + /// The password property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#nullable restore +#else + public string Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#endif + /// The phone property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#nullable restore +#else + public string Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#endif + /// The planet property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#nullable restore +#else + public string Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#endif + /// The profilePicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#nullable restore +#else + public string ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#endif + /// The tags property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Tags { + get { return BackingStore?.Get?>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#nullable restore +#else + public List Tags { + get { return BackingStore?.Get>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#endif + /// The userName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#nullable restore +#else + public string UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#endif + /// The validatedAt property + public DateTimeOffset? ValidatedAt { + get { return BackingStore?.Get("validatedAt"); } + set { BackingStore?.Set("validatedAt", value); } + } + /// The validatedAtDate property + public Date? ValidatedAtDate { + get { return BackingStore?.Get("validatedAtDate"); } + set { BackingStore?.Set("validatedAtDate", value); } + } + /// The validatedAtTime property + public Time? ValidatedAtTime { + get { return BackingStore?.Get("validatedAtTime"); } + set { BackingStore?.Set("validatedAtTime", value); } + } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountAttributesInPatchRequest() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountAttributesInPatchRequest CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountAttributesInPatchRequest(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"age", n => { Age = n.GetDoubleValue(); } }, + {"alternativeId", n => { AlternativeId = n.GetGuidValue(); } }, + {"backgroundPicture", n => { BackgroundPicture = n.GetStringValue(); } }, + {"countryCode", n => { CountryCode = n.GetStringValue(); } }, + {"creditCard", n => { CreditCard = n.GetStringValue(); } }, + {"email", n => { Email = n.GetStringValue(); } }, + {"firstName", n => { FirstName = n.GetStringValue(); } }, + {"lastName", n => { LastName = n.GetStringValue(); } }, + {"nextRevalidation", n => { NextRevalidation = n.GetStringValue(); } }, + {"password", n => { Password = n.GetStringValue(); } }, + {"phone", n => { Phone = n.GetStringValue(); } }, + {"planet", n => { Planet = n.GetStringValue(); } }, + {"profilePicture", n => { ProfilePicture = n.GetStringValue(); } }, + {"tags", n => { Tags = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"userName", n => { UserName = n.GetStringValue(); } }, + {"validatedAt", n => { ValidatedAt = n.GetDateTimeOffsetValue(); } }, + {"validatedAtDate", n => { ValidatedAtDate = n.GetDateValue(); } }, + {"validatedAtTime", n => { ValidatedAtTime = n.GetTimeValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteDoubleValue("age", Age); + writer.WriteGuidValue("alternativeId", AlternativeId); + writer.WriteStringValue("backgroundPicture", BackgroundPicture); + writer.WriteStringValue("countryCode", CountryCode); + writer.WriteStringValue("creditCard", CreditCard); + writer.WriteStringValue("email", Email); + writer.WriteStringValue("firstName", FirstName); + writer.WriteStringValue("lastName", LastName); + writer.WriteStringValue("nextRevalidation", NextRevalidation); + writer.WriteStringValue("password", Password); + writer.WriteStringValue("phone", Phone); + writer.WriteStringValue("planet", Planet); + writer.WriteStringValue("profilePicture", ProfilePicture); + writer.WriteCollectionOfPrimitiveValues("tags", Tags); + writer.WriteStringValue("userName", UserName); + writer.WriteDateTimeOffsetValue("validatedAt", ValidatedAt); + writer.WriteDateValue("validatedAtDate", ValidatedAtDate); + writer.WriteTimeValue("validatedAtTime", ValidatedAtTime); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPostRequest.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPostRequest.cs new file mode 100644 index 0000000000..f904b499d0 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInPostRequest.cs @@ -0,0 +1,295 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountAttributesInPostRequest : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The age property + public double? Age { + get { return BackingStore?.Get("age"); } + set { BackingStore?.Set("age", value); } + } + /// The alternativeId property + public Guid? AlternativeId { + get { return BackingStore?.Get("alternativeId"); } + set { BackingStore?.Set("alternativeId", value); } + } + /// The backgroundPicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#nullable restore +#else + public string BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The countryCode property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#nullable restore +#else + public string CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#endif + /// The creditCard property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#nullable restore +#else + public string CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#endif + /// The email property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#nullable restore +#else + public string Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#endif + /// The firstName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#nullable restore +#else + public string FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#endif + /// The lastName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#nullable restore +#else + public string LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#endif + /// The nextRevalidation property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#nullable restore +#else + public string NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#endif + /// The password property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#nullable restore +#else + public string Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#endif + /// The phone property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#nullable restore +#else + public string Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#endif + /// The planet property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#nullable restore +#else + public string Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#endif + /// The profilePicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#nullable restore +#else + public string ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#endif + /// The tags property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Tags { + get { return BackingStore?.Get?>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#nullable restore +#else + public List Tags { + get { return BackingStore?.Get>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#endif + /// The userName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#nullable restore +#else + public string UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#endif + /// The validatedAt property + public DateTimeOffset? ValidatedAt { + get { return BackingStore?.Get("validatedAt"); } + set { BackingStore?.Set("validatedAt", value); } + } + /// The validatedAtDate property + public Date? ValidatedAtDate { + get { return BackingStore?.Get("validatedAtDate"); } + set { BackingStore?.Set("validatedAtDate", value); } + } + /// The validatedAtTime property + public Time? ValidatedAtTime { + get { return BackingStore?.Get("validatedAtTime"); } + set { BackingStore?.Set("validatedAtTime", value); } + } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountAttributesInPostRequest() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountAttributesInPostRequest CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountAttributesInPostRequest(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"age", n => { Age = n.GetDoubleValue(); } }, + {"alternativeId", n => { AlternativeId = n.GetGuidValue(); } }, + {"backgroundPicture", n => { BackgroundPicture = n.GetStringValue(); } }, + {"countryCode", n => { CountryCode = n.GetStringValue(); } }, + {"creditCard", n => { CreditCard = n.GetStringValue(); } }, + {"email", n => { Email = n.GetStringValue(); } }, + {"firstName", n => { FirstName = n.GetStringValue(); } }, + {"lastName", n => { LastName = n.GetStringValue(); } }, + {"nextRevalidation", n => { NextRevalidation = n.GetStringValue(); } }, + {"password", n => { Password = n.GetStringValue(); } }, + {"phone", n => { Phone = n.GetStringValue(); } }, + {"planet", n => { Planet = n.GetStringValue(); } }, + {"profilePicture", n => { ProfilePicture = n.GetStringValue(); } }, + {"tags", n => { Tags = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"userName", n => { UserName = n.GetStringValue(); } }, + {"validatedAt", n => { ValidatedAt = n.GetDateTimeOffsetValue(); } }, + {"validatedAtDate", n => { ValidatedAtDate = n.GetDateValue(); } }, + {"validatedAtTime", n => { ValidatedAtTime = n.GetTimeValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteDoubleValue("age", Age); + writer.WriteGuidValue("alternativeId", AlternativeId); + writer.WriteStringValue("backgroundPicture", BackgroundPicture); + writer.WriteStringValue("countryCode", CountryCode); + writer.WriteStringValue("creditCard", CreditCard); + writer.WriteStringValue("email", Email); + writer.WriteStringValue("firstName", FirstName); + writer.WriteStringValue("lastName", LastName); + writer.WriteStringValue("nextRevalidation", NextRevalidation); + writer.WriteStringValue("password", Password); + writer.WriteStringValue("phone", Phone); + writer.WriteStringValue("planet", Planet); + writer.WriteStringValue("profilePicture", ProfilePicture); + writer.WriteCollectionOfPrimitiveValues("tags", Tags); + writer.WriteStringValue("userName", UserName); + writer.WriteDateTimeOffsetValue("validatedAt", ValidatedAt); + writer.WriteDateValue("validatedAtDate", ValidatedAtDate); + writer.WriteTimeValue("validatedAtTime", ValidatedAtTime); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInResponse.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInResponse.cs new file mode 100644 index 0000000000..1b759d3605 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountAttributesInResponse.cs @@ -0,0 +1,295 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using Microsoft.Kiota.Abstractions; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountAttributesInResponse : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The age property + public double? Age { + get { return BackingStore?.Get("age"); } + set { BackingStore?.Set("age", value); } + } + /// The alternativeId property + public Guid? AlternativeId { + get { return BackingStore?.Get("alternativeId"); } + set { BackingStore?.Set("alternativeId", value); } + } + /// The backgroundPicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#nullable restore +#else + public string BackgroundPicture { + get { return BackingStore?.Get("backgroundPicture"); } + set { BackingStore?.Set("backgroundPicture", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The countryCode property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#nullable restore +#else + public string CountryCode { + get { return BackingStore?.Get("countryCode"); } + set { BackingStore?.Set("countryCode", value); } + } +#endif + /// The creditCard property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#nullable restore +#else + public string CreditCard { + get { return BackingStore?.Get("creditCard"); } + set { BackingStore?.Set("creditCard", value); } + } +#endif + /// The email property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#nullable restore +#else + public string Email { + get { return BackingStore?.Get("email"); } + set { BackingStore?.Set("email", value); } + } +#endif + /// The firstName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#nullable restore +#else + public string FirstName { + get { return BackingStore?.Get("firstName"); } + set { BackingStore?.Set("firstName", value); } + } +#endif + /// The lastName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#nullable restore +#else + public string LastName { + get { return BackingStore?.Get("lastName"); } + set { BackingStore?.Set("lastName", value); } + } +#endif + /// The nextRevalidation property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#nullable restore +#else + public string NextRevalidation { + get { return BackingStore?.Get("nextRevalidation"); } + set { BackingStore?.Set("nextRevalidation", value); } + } +#endif + /// The password property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#nullable restore +#else + public string Password { + get { return BackingStore?.Get("password"); } + set { BackingStore?.Set("password", value); } + } +#endif + /// The phone property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#nullable restore +#else + public string Phone { + get { return BackingStore?.Get("phone"); } + set { BackingStore?.Set("phone", value); } + } +#endif + /// The planet property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#nullable restore +#else + public string Planet { + get { return BackingStore?.Get("planet"); } + set { BackingStore?.Set("planet", value); } + } +#endif + /// The profilePicture property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#nullable restore +#else + public string ProfilePicture { + get { return BackingStore?.Get("profilePicture"); } + set { BackingStore?.Set("profilePicture", value); } + } +#endif + /// The tags property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Tags { + get { return BackingStore?.Get?>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#nullable restore +#else + public List Tags { + get { return BackingStore?.Get>("tags"); } + set { BackingStore?.Set("tags", value); } + } +#endif + /// The userName property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#nullable restore +#else + public string UserName { + get { return BackingStore?.Get("userName"); } + set { BackingStore?.Set("userName", value); } + } +#endif + /// The validatedAt property + public DateTimeOffset? ValidatedAt { + get { return BackingStore?.Get("validatedAt"); } + set { BackingStore?.Set("validatedAt", value); } + } + /// The validatedAtDate property + public Date? ValidatedAtDate { + get { return BackingStore?.Get("validatedAtDate"); } + set { BackingStore?.Set("validatedAtDate", value); } + } + /// The validatedAtTime property + public Time? ValidatedAtTime { + get { return BackingStore?.Get("validatedAtTime"); } + set { BackingStore?.Set("validatedAtTime", value); } + } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountAttributesInResponse() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountAttributesInResponse CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountAttributesInResponse(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"age", n => { Age = n.GetDoubleValue(); } }, + {"alternativeId", n => { AlternativeId = n.GetGuidValue(); } }, + {"backgroundPicture", n => { BackgroundPicture = n.GetStringValue(); } }, + {"countryCode", n => { CountryCode = n.GetStringValue(); } }, + {"creditCard", n => { CreditCard = n.GetStringValue(); } }, + {"email", n => { Email = n.GetStringValue(); } }, + {"firstName", n => { FirstName = n.GetStringValue(); } }, + {"lastName", n => { LastName = n.GetStringValue(); } }, + {"nextRevalidation", n => { NextRevalidation = n.GetStringValue(); } }, + {"password", n => { Password = n.GetStringValue(); } }, + {"phone", n => { Phone = n.GetStringValue(); } }, + {"planet", n => { Planet = n.GetStringValue(); } }, + {"profilePicture", n => { ProfilePicture = n.GetStringValue(); } }, + {"tags", n => { Tags = n.GetCollectionOfPrimitiveValues()?.ToList(); } }, + {"userName", n => { UserName = n.GetStringValue(); } }, + {"validatedAt", n => { ValidatedAt = n.GetDateTimeOffsetValue(); } }, + {"validatedAtDate", n => { ValidatedAtDate = n.GetDateValue(); } }, + {"validatedAtTime", n => { ValidatedAtTime = n.GetTimeValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteDoubleValue("age", Age); + writer.WriteGuidValue("alternativeId", AlternativeId); + writer.WriteStringValue("backgroundPicture", BackgroundPicture); + writer.WriteStringValue("countryCode", CountryCode); + writer.WriteStringValue("creditCard", CreditCard); + writer.WriteStringValue("email", Email); + writer.WriteStringValue("firstName", FirstName); + writer.WriteStringValue("lastName", LastName); + writer.WriteStringValue("nextRevalidation", NextRevalidation); + writer.WriteStringValue("password", Password); + writer.WriteStringValue("phone", Phone); + writer.WriteStringValue("planet", Planet); + writer.WriteStringValue("profilePicture", ProfilePicture); + writer.WriteCollectionOfPrimitiveValues("tags", Tags); + writer.WriteStringValue("userName", UserName); + writer.WriteDateTimeOffsetValue("validatedAt", ValidatedAt); + writer.WriteDateValue("validatedAtDate", ValidatedAtDate); + writer.WriteTimeValue("validatedAtTime", ValidatedAtTime); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPatchRequest.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPatchRequest.cs new file mode 100644 index 0000000000..ab2367b343 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPatchRequest.cs @@ -0,0 +1,90 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountDataInPatchRequest : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The attributes property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountAttributesInPatchRequest? Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#nullable restore +#else + public SocialMediaAccountAttributesInPatchRequest Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The id property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public string? Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#nullable restore +#else + public string Id { + get { return BackingStore?.Get("id"); } + set { BackingStore?.Set("id", value); } + } +#endif + /// The type property + public SocialMediaAccountResourceType? Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountDataInPatchRequest() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountDataInPatchRequest CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountDataInPatchRequest(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"attributes", n => { Attributes = n.GetObjectValue(SocialMediaAccountAttributesInPatchRequest.CreateFromDiscriminatorValue); } }, + {"id", n => { Id = n.GetStringValue(); } }, + {"type", n => { Type = n.GetEnumValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue("attributes", Attributes); + writer.WriteStringValue("id", Id); + writer.WriteEnumValue("type", Type); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPostRequest.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPostRequest.cs new file mode 100644 index 0000000000..a6cf4814d5 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInPostRequest.cs @@ -0,0 +1,74 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountDataInPostRequest : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// The attributes property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountAttributesInPostRequest? Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#nullable restore +#else + public SocialMediaAccountAttributesInPostRequest Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#endif + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The type property + public SocialMediaAccountResourceType? Type { + get { return BackingStore?.Get("type"); } + set { BackingStore?.Set("type", value); } + } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountDataInPostRequest() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountDataInPostRequest CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountDataInPostRequest(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"attributes", n => { Attributes = n.GetObjectValue(SocialMediaAccountAttributesInPostRequest.CreateFromDiscriminatorValue); } }, + {"type", n => { Type = n.GetEnumValue(); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue("attributes", Attributes); + writer.WriteEnumValue("type", Type); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse.cs new file mode 100644 index 0000000000..7344e1f916 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse.cs @@ -0,0 +1,90 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountDataInResponse : DataInResponse, IParsable + #pragma warning restore CS1591 + { + /// The attributes property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountAttributesInResponse? Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#nullable restore +#else + public SocialMediaAccountAttributesInResponse Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#endif + /// The links property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ResourceLinks? Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#nullable restore +#else + public ResourceLinks Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#endif + /// The meta property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountDataInResponse_meta? Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#nullable restore +#else + public SocialMediaAccountDataInResponse_meta Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#endif + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static new SocialMediaAccountDataInResponse CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountDataInResponse(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public override IDictionary> GetFieldDeserializers() + { + return new Dictionary>(base.GetFieldDeserializers()) + { + {"attributes", n => { Attributes = n.GetObjectValue(SocialMediaAccountAttributesInResponse.CreateFromDiscriminatorValue); } }, + {"links", n => { Links = n.GetObjectValue(ResourceLinks.CreateFromDiscriminatorValue); } }, + {"meta", n => { Meta = n.GetObjectValue(SocialMediaAccountDataInResponse_meta.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public override void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + base.Serialize(writer); + writer.WriteObjectValue("attributes", Attributes); + writer.WriteObjectValue("links", Links); + writer.WriteObjectValue("meta", Meta); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse_meta.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse_meta.cs new file mode 100644 index 0000000000..daa2f4eaf2 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountDataInResponse_meta.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountDataInResponse_meta : IAdditionalDataHolder, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { + get { return BackingStore.Get>("AdditionalData") ?? throw new InvalidOperationException("AdditionalData can not be null"); } + set { BackingStore.Set("AdditionalData", value); } + } + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountDataInResponse_meta() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountDataInResponse_meta CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountDataInResponse_meta(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPatchRequestDocument.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPatchRequestDocument.cs new file mode 100644 index 0000000000..4dbdff98e6 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPatchRequestDocument.cs @@ -0,0 +1,67 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountPatchRequestDocument : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The data property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountDataInPatchRequest? Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#nullable restore +#else + public SocialMediaAccountDataInPatchRequest Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountPatchRequestDocument() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountPatchRequestDocument CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountPatchRequestDocument(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"data", n => { Data = n.GetObjectValue(SocialMediaAccountDataInPatchRequest.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue("data", Data); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPostRequestDocument.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPostRequestDocument.cs new file mode 100644 index 0000000000..91c75791bf --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPostRequestDocument.cs @@ -0,0 +1,67 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountPostRequestDocument : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The data property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountDataInPostRequest? Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#nullable restore +#else + public SocialMediaAccountDataInPostRequest Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountPostRequestDocument() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountPostRequestDocument CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountPostRequestDocument(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"data", n => { Data = n.GetObjectValue(SocialMediaAccountDataInPostRequest.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue("data", Data); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument.cs new file mode 100644 index 0000000000..8bcf6b5010 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument.cs @@ -0,0 +1,115 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountPrimaryResponseDocument : IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// The data property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccounts? Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#nullable restore +#else + public SocialMediaAccounts Data { + get { return BackingStore?.Get("data"); } + set { BackingStore?.Set("data", value); } + } +#endif + /// The included property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public List? Included { + get { return BackingStore?.Get?>("included"); } + set { BackingStore?.Set("included", value); } + } +#nullable restore +#else + public List Included { + get { return BackingStore?.Get>("included"); } + set { BackingStore?.Set("included", value); } + } +#endif + /// The links property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ResourceTopLevelLinks? Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#nullable restore +#else + public ResourceTopLevelLinks Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#endif + /// The meta property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountPrimaryResponseDocument_meta? Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#nullable restore +#else + public SocialMediaAccountPrimaryResponseDocument_meta Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#endif + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountPrimaryResponseDocument() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountPrimaryResponseDocument CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountPrimaryResponseDocument(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + {"data", n => { Data = n.GetObjectValue(SocialMediaAccounts.CreateFromDiscriminatorValue); } }, + {"included", n => { Included = n.GetCollectionOfObjectValues(DataInResponse.CreateFromDiscriminatorValue)?.ToList(); } }, + {"links", n => { Links = n.GetObjectValue(ResourceTopLevelLinks.CreateFromDiscriminatorValue); } }, + {"meta", n => { Meta = n.GetObjectValue(SocialMediaAccountPrimaryResponseDocument_meta.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteObjectValue("data", Data); + writer.WriteCollectionOfObjectValues("included", Included); + writer.WriteObjectValue("links", Links); + writer.WriteObjectValue("meta", Meta); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument_meta.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument_meta.cs new file mode 100644 index 0000000000..94efbdee26 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountPrimaryResponseDocument_meta.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccountPrimaryResponseDocument_meta : IAdditionalDataHolder, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { + get { return BackingStore.Get>("AdditionalData") ?? throw new InvalidOperationException("AdditionalData can not be null"); } + set { BackingStore.Set("AdditionalData", value); } + } + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccountPrimaryResponseDocument_meta() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccountPrimaryResponseDocument_meta CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccountPrimaryResponseDocument_meta(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountResourceType.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountResourceType.cs new file mode 100644 index 0000000000..da2c2ce427 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccountResourceType.cs @@ -0,0 +1,14 @@ +// +using System.Runtime.Serialization; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public enum SocialMediaAccountResourceType + #pragma warning restore CS1591 + { + [EnumMember(Value = "socialMediaAccounts")] + #pragma warning disable CS1591 + SocialMediaAccounts, + #pragma warning restore CS1591 + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts.cs new file mode 100644 index 0000000000..9c56d76975 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts.cs @@ -0,0 +1,90 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccounts : DataInResponse, IParsable + #pragma warning restore CS1591 + { + /// The attributes property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccountAttributesInResponse? Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#nullable restore +#else + public SocialMediaAccountAttributesInResponse Attributes { + get { return BackingStore?.Get("attributes"); } + set { BackingStore?.Set("attributes", value); } + } +#endif + /// The links property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public ResourceLinks? Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#nullable restore +#else + public ResourceLinks Links { + get { return BackingStore?.Get("links"); } + set { BackingStore?.Set("links", value); } + } +#endif + /// The meta property +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public SocialMediaAccounts_meta? Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#nullable restore +#else + public SocialMediaAccounts_meta Meta { + get { return BackingStore?.Get("meta"); } + set { BackingStore?.Set("meta", value); } + } +#endif + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static new SocialMediaAccounts CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccounts(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public override IDictionary> GetFieldDeserializers() + { + return new Dictionary>(base.GetFieldDeserializers()) + { + {"attributes", n => { Attributes = n.GetObjectValue(SocialMediaAccountAttributesInResponse.CreateFromDiscriminatorValue); } }, + {"links", n => { Links = n.GetObjectValue(ResourceLinks.CreateFromDiscriminatorValue); } }, + {"meta", n => { Meta = n.GetObjectValue(SocialMediaAccounts_meta.CreateFromDiscriminatorValue); } }, + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public override void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + base.Serialize(writer); + writer.WriteObjectValue("attributes", Attributes); + writer.WriteObjectValue("links", Links); + writer.WriteObjectValue("meta", Meta); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts_meta.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts_meta.cs new file mode 100644 index 0000000000..0f2bcb4057 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/Models/SocialMediaAccounts_meta.cs @@ -0,0 +1,58 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions.Store; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models { + #pragma warning disable CS1591 + public class SocialMediaAccounts_meta : IAdditionalDataHolder, IBackedModel, IParsable + #pragma warning restore CS1591 + { + /// Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + public IDictionary AdditionalData { + get { return BackingStore.Get>("AdditionalData") ?? throw new InvalidOperationException("AdditionalData can not be null"); } + set { BackingStore.Set("AdditionalData", value); } + } + /// Stores model information. + public IBackingStore BackingStore { get; private set; } + /// + /// Instantiates a new and sets the default values. + /// + public SocialMediaAccounts_meta() + { + BackingStore = BackingStoreFactorySingleton.Instance.CreateBackingStore(); + AdditionalData = new Dictionary(); + } + /// + /// Creates a new instance of the appropriate class based on discriminator value + /// + /// A + /// The parse node to use to read the discriminator value and create the object + public static SocialMediaAccounts_meta CreateFromDiscriminatorValue(IParseNode parseNode) + { + _ = parseNode ?? throw new ArgumentNullException(nameof(parseNode)); + return new SocialMediaAccounts_meta(); + } + /// + /// The deserialization information for the current model + /// + /// A IDictionary<string, Action<IParseNode>> + public virtual IDictionary> GetFieldDeserializers() + { + return new Dictionary> + { + }; + } + /// + /// Serializes information the current object + /// + /// Serialization writer to use to serialize this model + public virtual void Serialize(ISerializationWriter writer) + { + _ = writer ?? throw new ArgumentNullException(nameof(writer)); + writer.WriteAdditionalData(AdditionalData); + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/Item/SocialMediaAccountsItemRequestBuilder.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/Item/SocialMediaAccountsItemRequestBuilder.cs new file mode 100644 index 0000000000..0f32286f7e --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/Item/SocialMediaAccountsItemRequestBuilder.cs @@ -0,0 +1,112 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Threading; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.SocialMediaAccounts.Item { + /// + /// Builds and executes requests for operations under \socialMediaAccounts\{id} + /// + public class SocialMediaAccountsItemRequestBuilder : BaseRequestBuilder + { + /// + /// Instantiates a new and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public SocialMediaAccountsItemRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/socialMediaAccounts/{id}{?query*}", pathParameters) + { + } + /// + /// Instantiates a new and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public SocialMediaAccountsItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/socialMediaAccounts/{id}{?query*}", rawUrl) + { + } + /// + /// Updates an existing socialMediaAccount. + /// + /// A + /// The request body + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. + /// When receiving a 400 status code + /// When receiving a 404 status code + /// When receiving a 409 status code + /// When receiving a 422 status code +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task PatchAsync(SocialMediaAccountPatchRequestDocument body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task PatchAsync(SocialMediaAccountPatchRequestDocument body, Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = ToPatchRequestInformation(body, requestConfiguration); + var errorMapping = new Dictionary> + { + {"400", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"404", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"409", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"422", ErrorResponseDocument.CreateFromDiscriminatorValue}, + }; + return await RequestAdapter.SendAsync(requestInfo, SocialMediaAccountPrimaryResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false); + } + /// + /// Updates an existing socialMediaAccount. + /// + /// A + /// The request body + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToPatchRequestInformation(SocialMediaAccountPatchRequestDocument body, Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToPatchRequestInformation(SocialMediaAccountPatchRequestDocument body, Action> requestConfiguration = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = new RequestInformation(Method.PATCH, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json"); + requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json", body); + return requestInfo; + } + /// + /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored. + /// + /// A + /// The raw URL to use for the request builder. + public SocialMediaAccountsItemRequestBuilder WithUrl(string rawUrl) + { + return new SocialMediaAccountsItemRequestBuilder(rawUrl, RequestAdapter); + } + /// + /// Updates an existing socialMediaAccount. + /// + public class SocialMediaAccountsItemRequestBuilderPatchQueryParameters + { + /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + [QueryParameter("query")] + public string? Query { get; set; } +#nullable restore +#else + [QueryParameter("query")] + public string Query { get; set; } +#endif + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/SocialMediaAccountsRequestBuilder.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/SocialMediaAccountsRequestBuilder.cs new file mode 100644 index 0000000000..57524088f0 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/SocialMediaAccounts/SocialMediaAccountsRequestBuilder.cs @@ -0,0 +1,127 @@ +// +using Microsoft.Kiota.Abstractions.Serialization; +using Microsoft.Kiota.Abstractions; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.SocialMediaAccounts.Item; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Threading; +using System; +namespace OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.SocialMediaAccounts { + /// + /// Builds and executes requests for operations under \socialMediaAccounts + /// + public class SocialMediaAccountsRequestBuilder : BaseRequestBuilder + { + /// Gets an item from the OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.socialMediaAccounts.item collection + /// The identifier of the socialMediaAccount to update. + /// A + public SocialMediaAccountsItemRequestBuilder this[string position] + { + get + { + var urlTplParams = new Dictionary(PathParameters); + urlTplParams.Add("id", position); + return new SocialMediaAccountsItemRequestBuilder(urlTplParams, RequestAdapter); + } + } + /// + /// Instantiates a new and sets the default values. + /// + /// Path parameters for the request + /// The request adapter to use to execute the requests. + public SocialMediaAccountsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/socialMediaAccounts{?query*}", pathParameters) + { + } + /// + /// Instantiates a new and sets the default values. + /// + /// The raw URL to use for the request builder. + /// The request adapter to use to execute the requests. + public SocialMediaAccountsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/socialMediaAccounts{?query*}", rawUrl) + { + } + /// + /// Creates a new socialMediaAccount. + /// + /// A + /// The request body + /// Cancellation token to use when cancelling requests + /// Configuration for the request such as headers, query parameters, and middleware options. + /// When receiving a 400 status code + /// When receiving a 403 status code + /// When receiving a 404 status code + /// When receiving a 409 status code + /// When receiving a 422 status code +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public async Task PostAsync(SocialMediaAccountPostRequestDocument body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default) + { +#nullable restore +#else + public async Task PostAsync(SocialMediaAccountPostRequestDocument body, Action> requestConfiguration = default, CancellationToken cancellationToken = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = ToPostRequestInformation(body, requestConfiguration); + var errorMapping = new Dictionary> + { + {"400", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"403", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"404", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"409", ErrorResponseDocument.CreateFromDiscriminatorValue}, + {"422", ErrorResponseDocument.CreateFromDiscriminatorValue}, + }; + return await RequestAdapter.SendAsync(requestInfo, SocialMediaAccountPrimaryResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false); + } + /// + /// Creates a new socialMediaAccount. + /// + /// A + /// The request body + /// Configuration for the request such as headers, query parameters, and middleware options. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + public RequestInformation ToPostRequestInformation(SocialMediaAccountPostRequestDocument body, Action>? requestConfiguration = default) + { +#nullable restore +#else + public RequestInformation ToPostRequestInformation(SocialMediaAccountPostRequestDocument body, Action> requestConfiguration = default) + { +#endif + _ = body ?? throw new ArgumentNullException(nameof(body)); + var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters); + requestInfo.Configure(requestConfiguration); + requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json"); + requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json", body); + return requestInfo; + } + /// + /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored. + /// + /// A + /// The raw URL to use for the request builder. + public SocialMediaAccountsRequestBuilder WithUrl(string rawUrl) + { + return new SocialMediaAccountsRequestBuilder(rawUrl, RequestAdapter); + } + /// + /// Creates a new socialMediaAccount. + /// + public class SocialMediaAccountsRequestBuilderPostQueryParameters + { + /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters. +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +#nullable enable + [QueryParameter("query")] + public string? Query { get; set; } +#nullable restore +#else + [QueryParameter("query")] + public string Query { get; set; } +#endif + } + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/ModelStateValidationTests.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/ModelStateValidationTests.cs new file mode 100644 index 0000000000..3f7069c1fb --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/ModelStateValidationTests.cs @@ -0,0 +1,592 @@ +using FluentAssertions; +using JsonApiDotNetCore.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Kiota.Http.HttpClientLibrary; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode; +using OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode.Models; +using OpenApiTests; +using OpenApiTests.ModelStateValidation; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiKiotaEndToEndTests.ModelStateValidation; + +public sealed class ModelStateValidationTests + : IClassFixture, ModelStateValidationDbContext>> +{ + private readonly IntegrationTestContext, ModelStateValidationDbContext> _testContext; + private readonly TestableHttpClientRequestAdapterFactory _requestAdapterFactory; + private readonly ModelStateValidationFakers _fakers = new(); + + public ModelStateValidationTests(IntegrationTestContext, ModelStateValidationDbContext> testContext, + ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _requestAdapterFactory = new TestableHttpClientRequestAdapterFactory(testOutputHelper); + + testContext.UseController(); + + var options = testContext.Factory.Services.GetRequiredService(); + options.SerializerOptions.Converters.Add(new UtcDateTimeJsonConverter()); + } + + [Theory] + [InlineData("a")] + [InlineData("abcdefghijklmnopqrstu")] + public async Task Cannot_exceed_length_constraint(string firstName) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + FirstName = firstName, + LastName = newAccount.LastName + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field FirstName must be a string or collection type with a minimum length of '2' and maximum length of '20'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/firstName"); + } + + [Theory] + [InlineData("ab")] + [InlineData("abcdefghijklmnopqrs")] + public async Task Cannot_exceed_string_length_constraint(string userName) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + UserName = userName + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field UserName must be a string with a minimum length of 3 and a maximum length of 18."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/userName"); + } + + [Fact] + public async Task Cannot_violate_regular_expression_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + UserName = "aB1" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("Only letters are allowed."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/userName"); + } + + [Fact] + public async Task Cannot_use_invalid_credit_card_number() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + CreditCard = "123-456" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The CreditCard field is not a valid credit card number."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/creditCard"); + } + + [Fact] + public async Task Cannot_use_invalid_email_address() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Email = "abc" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Email field is not a valid e-mail address."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/email"); + } + + [Fact] + public async Task Cannot_exceed_min_length_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "YQ==" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Password must be a string or array type with a minimum length of '5'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Fact] + public async Task Cannot_exceed_max_length_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Password must be a string or array type with a maximum length of '100'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Fact] + public async Task Cannot_use_invalid_base64() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "not_base_64" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Password field is not a valid Base64 encoding."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Theory] + [InlineData(-1)] + [InlineData(-0.56)] + [InlineData(123.98)] + [InlineData(124)] + public async Task Cannot_use_double_outside_of_valid_range(double age) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Age = age + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be($"The field Age must be between {0.1} exclusive and {122.9} exclusive."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/age"); + } + + [Fact] + public async Task Cannot_use_relative_url() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + BackgroundPicture = "relativeurl" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The BackgroundPicture field is not a valid fully-qualified http, https, or ftp URL."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/backgroundPicture"); + } + + [Theory] + [InlineData(0)] + [InlineData(11)] + public async Task Cannot_exceed_collection_length_constraint(int length) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Tags = Enumerable.Repeat("-", length).ToList() + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Tags must be a string or collection type with a minimum length of '1' and maximum length of '10'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/tags"); + } + + [Fact] + public async Task Cannot_use_non_allowed_value() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + CountryCode = "XX" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The CountryCode field does not equal any of the values specified in AllowedValuesAttribute."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/countryCode"); + } + + [Fact] + public async Task Cannot_use_denied_value() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Planet = "pluto" + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Planet field equals one of the values specified in DeniedValuesAttribute."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/planet"); + } + + [Fact] + public async Task Cannot_use_TimeSpan_outside_of_valid_range() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + NextRevalidation = TimeSpan.FromSeconds(1).ToString() + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync()).Which; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field NextRevalidation must be between 01:00:00 and 05:00:00."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/nextRevalidation"); + } + + [Fact] + public async Task Can_create_resource_with_valid_properties() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClientRequestAdapter requestAdapter = _requestAdapterFactory.CreateAdapter(_testContext.Factory); + ModelStateValidationClient apiClient = new(requestAdapter); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + AlternativeId = newAccount.AlternativeId, + FirstName = newAccount.FirstName, + LastName = newAccount.LastName, + UserName = newAccount.UserName, + CreditCard = newAccount.CreditCard, + Email = newAccount.Email, + Password = newAccount.Password, + Phone = newAccount.Phone, + Age = newAccount.Age, + ProfilePicture = newAccount.ProfilePicture!.ToString(), + BackgroundPicture = newAccount.BackgroundPicture, + Tags = newAccount.Tags, + CountryCode = newAccount.CountryCode, + Planet = newAccount.Planet, + NextRevalidation = newAccount.NextRevalidation!.Value.ToString(), + ValidatedAt = newAccount.ValidatedAt!, + ValidatedAtDate = newAccount.ValidatedAtDate!.Value, + ValidatedAtTime = newAccount.ValidatedAtTime!.Value + } + } + }; + + // Act + Func action = () => apiClient.SocialMediaAccounts.PostAsync(requestBody); + + // Assert + await action.Should().NotThrowAsync(); + } +} diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs new file mode 100644 index 0000000000..d4d22fa27e --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenApiKiotaEndToEndTests.ModelStateValidation; + +internal sealed class UtcDateTimeJsonConverter : JsonConverter +{ + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + DateTimeOffset dateTimeOffset = DateTimeOffset.Parse(reader.GetString()!); + return dateTimeOffset.UtcDateTime; + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToUniversalTime().ToString("O")); + } +} diff --git a/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj b/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj index a66c69869b..abb1cd95ce 100644 --- a/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj +++ b/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj @@ -32,6 +32,8 @@ Command="dotnet kiota generate --language CSharp --class-name QueryStringsClient --namespace-name OpenApiKiotaEndToEndTests.QueryStrings.GeneratedCode --output ./QueryStrings/GeneratedCode --backing-store --exclude-backward-compatible --clean-output --clear-cache --log-level Error --openapi ../OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json" /> + , ModelStateValidationDbContext>> +{ + private readonly IntegrationTestContext, ModelStateValidationDbContext> _testContext; + private readonly XUnitLogHttpMessageHandler _logHttpMessageHandler; + private readonly ModelStateValidationFakers _fakers = new(); + + public ModelStateValidationTests(IntegrationTestContext, ModelStateValidationDbContext> testContext, + ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _logHttpMessageHandler = new XUnitLogHttpMessageHandler(testOutputHelper); + + testContext.UseController(); + + var options = testContext.Factory.Services.GetRequiredService(); + options.SerializerOptions.Converters.Add(new UtcDateTimeJsonConverter()); + } + + [Theory] + [InlineData("a")] + [InlineData("abcdefghijklmnopqrstu")] + public async Task Cannot_exceed_length_constraint(string firstName) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + FirstName = firstName, + LastName = newAccount.LastName + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field FirstName must be a string or collection type with a minimum length of '2' and maximum length of '20'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/firstName"); + } + + [Theory] + [InlineData("ab")] + [InlineData("abcdefghijklmnopqrs")] + public async Task Cannot_exceed_string_length_constraint(string userName) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + UserName = userName + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field UserName must be a string with a minimum length of 3 and a maximum length of 18."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/userName"); + } + + [Fact] + public async Task Cannot_violate_regular_expression_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + UserName = "aB1" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("Only letters are allowed."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/userName"); + } + + [Fact] + public async Task Cannot_use_invalid_credit_card_number() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + CreditCard = "123-456" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The CreditCard field is not a valid credit card number."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/creditCard"); + } + + [Fact] + public async Task Cannot_use_invalid_email_address() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Email = "abc" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Email field is not a valid e-mail address."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/email"); + } + + [Fact] + public async Task Cannot_exceed_min_length_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "YQ==" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Password must be a string or array type with a minimum length of '5'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Fact] + public async Task Cannot_exceed_max_length_constraint() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Password must be a string or array type with a maximum length of '100'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Fact] + public async Task Cannot_use_invalid_base64() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Type = SocialMediaAccountResourceType.SocialMediaAccounts, + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Password = "not_base_64" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Password field is not a valid Base64 encoding."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/password"); + } + + [Theory] + [InlineData(-1)] + [InlineData(-0.56)] + [InlineData(123.98)] + [InlineData(124)] + public async Task Cannot_use_double_outside_of_valid_range(double age) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Age = age + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be($"The field Age must be between {0.1} exclusive and {122.9} exclusive."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/age"); + } + + [Fact] + public async Task Cannot_use_relative_url() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + BackgroundPicture = new Uri("relativeurl", UriKind.Relative) + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The BackgroundPicture field is not a valid fully-qualified http, https, or ftp URL."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/backgroundPicture"); + } + + [Theory] + [InlineData(0)] + [InlineData(11)] + public async Task Cannot_exceed_collection_length_constraint(int length) + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Tags = Enumerable.Repeat("-", length).ToList() + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field Tags must be a string or collection type with a minimum length of '1' and maximum length of '10'."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/tags"); + } + + [Fact] + public async Task Cannot_use_non_allowed_value() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + CountryCode = "XX" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The CountryCode field does not equal any of the values specified in AllowedValuesAttribute."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/countryCode"); + } + + [Fact] + public async Task Cannot_use_denied_value() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + Planet = "pluto" + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The Planet field equals one of the values specified in DeniedValuesAttribute."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/planet"); + } + + [Fact] + public async Task Cannot_use_TimeSpan_outside_of_valid_range() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + LastName = newAccount.LastName, + NextRevalidation = TimeSpan.FromSeconds(1).ToString() + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + ErrorResponseDocument document = (await action.Should().ThrowExactlyAsync>()).Which.Result; + document.Errors.ShouldHaveCount(1); + + ErrorObject errorObject = document.Errors.First(); + errorObject.Title.Should().Be("Input validation failed."); + errorObject.Detail.Should().Be("The field NextRevalidation must be between 01:00:00 and 05:00:00."); + errorObject.Source.ShouldNotBeNull(); + errorObject.Source.Pointer.Should().Be("/data/attributes/nextRevalidation"); + } + + [Fact] + public async Task Can_create_resource_with_valid_properties() + { + // Arrange + SocialMediaAccount newAccount = _fakers.SocialMediaAccount.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateDefaultClient(_logHttpMessageHandler); + ModelStateValidationClient apiClient = new(httpClient); + + SocialMediaAccountPostRequestDocument requestBody = new() + { + Data = new SocialMediaAccountDataInPostRequest + { + Attributes = new SocialMediaAccountAttributesInPostRequest + { + AlternativeId = newAccount.AlternativeId, + FirstName = newAccount.FirstName, + LastName = newAccount.LastName, + UserName = newAccount.UserName, + CreditCard = newAccount.CreditCard, + Email = newAccount.Email, + Password = newAccount.Password, + Phone = newAccount.Phone, + Age = newAccount.Age, + ProfilePicture = newAccount.ProfilePicture, + BackgroundPicture = new Uri(newAccount.BackgroundPicture!), + Tags = newAccount.Tags, + CountryCode = newAccount.CountryCode, + Planet = newAccount.Planet, + NextRevalidation = newAccount.NextRevalidation!.Value.ToString(), + ValidatedAt = newAccount.ValidatedAt!, + ValidatedAtDate = new DateTimeOffset(newAccount.ValidatedAtDate!.Value.ToDateTime(new TimeOnly()), TimeSpan.Zero), + ValidatedAtTime = newAccount.ValidatedAtTime!.Value.ToTimeSpan() + } + } + }; + + // Act + Func action = () => apiClient.PostSocialMediaAccountAsync(requestBody); + + // Assert + await action.Should().NotThrowAsync(); + } +} diff --git a/test/OpenApiNSwagEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs b/test/OpenApiNSwagEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs new file mode 100644 index 0000000000..c0c1d04ffb --- /dev/null +++ b/test/OpenApiNSwagEndToEndTests/ModelStateValidation/UtcDateTimeJsonConverter.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenApiNSwagEndToEndTests.ModelStateValidation; + +internal sealed class UtcDateTimeJsonConverter : JsonConverter +{ + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + DateTimeOffset dateTimeOffset = DateTimeOffset.Parse(reader.GetString()!); + return dateTimeOffset.UtcDateTime; + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToUniversalTime().ToString("O")); + } +} diff --git a/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj b/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj index 18ea09354c..8277343c19 100644 --- a/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj +++ b/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj @@ -30,6 +30,13 @@ NSwagCSharp /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag /GenerateNullableReferenceTypes:true + + OpenApiNSwagEndToEndTests.ModelStateValidation.GeneratedCode + ModelStateValidationClient + ModelStateValidationClient.cs + NSwagCSharp + /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.NSwag /GenerateNullableReferenceTypes:true /GenerateOptionalParameters:true + OpenApiNSwagEndToEndTests.Headers.GeneratedCode HeadersClient diff --git a/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net6.0/swagger.g.json b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net6.0/swagger.g.json new file mode 100644 index 0000000000..efc8773a85 --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net6.0/swagger.g.json @@ -0,0 +1,846 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/socialMediaAccounts": { + "post": { + "tags": [ + "socialMediaAccounts" + ], + "summary": "Creates a new socialMediaAccount.", + "operationId": "postSocialMediaAccount", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the socialMediaAccount to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountPostRequestDocument" + } + ] + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "The socialMediaAccount was successfully created, which resulted in additional changes. The newly created socialMediaAccount is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created socialMediaAccount can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/socialMediaAccountPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The socialMediaAccount was successfully created, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "A related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "The request body contains conflicting information or another resource with the same ID already exists.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/socialMediaAccounts/{id}": { + "patch": { + "tags": [ + "socialMediaAccounts" + ], + "summary": "Updates an existing socialMediaAccount.", + "operationId": "patchSocialMediaAccount", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the socialMediaAccount to update.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the socialMediaAccount to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountPatchRequestDocument" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "The socialMediaAccount was successfully updated, which resulted in additional changes. The updated socialMediaAccount is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/socialMediaAccountPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The socialMediaAccount was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The socialMediaAccount or a related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "dataInResponse": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "type", + "mapping": { + "socialMediaAccounts": "#/components/schemas/socialMediaAccountDataInResponse" + } + }, + "x-abstract": true + }, + "errorLinks": { + "type": "object", + "properties": { + "about": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorObject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorLinks" + } + ], + "nullable": true + }, + "status": { + "type": "string" + }, + "code": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/errorSource" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { }, + "nullable": true + } + }, + "additionalProperties": false + }, + "errorResponseDocument": { + "required": [ + "errors", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorTopLevelLinks" + } + ] + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/errorObject" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "errorSource": { + "type": "object", + "properties": { + "pointer": { + "type": "string", + "nullable": true + }, + "parameter": { + "type": "string", + "nullable": true + }, + "header": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorTopLevelLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "resourceLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "resourceTopLevelLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInPatchRequest": { + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInPostRequest": { + "required": [ + "lastName" + ], + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInResponse": { + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/socialMediaAccountResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/socialMediaAccountResourceType" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/resourceLinks" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + ], + "additionalProperties": false + }, + "socialMediaAccountPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/resourceTopLevelLinks" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInResponse" + } + ] + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "socialMediaAccountResourceType": { + "enum": [ + "socialMediaAccounts" + ], + "type": "string", + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net8.0/swagger.g.json b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net8.0/swagger.g.json new file mode 100644 index 0000000000..eaf1c6dd5c --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/net8.0/swagger.g.json @@ -0,0 +1,864 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/socialMediaAccounts": { + "post": { + "tags": [ + "socialMediaAccounts" + ], + "summary": "Creates a new socialMediaAccount.", + "operationId": "postSocialMediaAccount", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the socialMediaAccount to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountPostRequestDocument" + } + ] + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "The socialMediaAccount was successfully created, which resulted in additional changes. The newly created socialMediaAccount is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created socialMediaAccount can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/socialMediaAccountPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The socialMediaAccount was successfully created, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "A related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "The request body contains conflicting information or another resource with the same ID already exists.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/socialMediaAccounts/{id}": { + "patch": { + "tags": [ + "socialMediaAccounts" + ], + "summary": "Updates an existing socialMediaAccount.", + "operationId": "patchSocialMediaAccount", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the socialMediaAccount to update.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the socialMediaAccount to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountPatchRequestDocument" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "The socialMediaAccount was successfully updated, which resulted in additional changes. The updated socialMediaAccount is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/socialMediaAccountPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The socialMediaAccount was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The socialMediaAccount or a related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "dataInResponse": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "type", + "mapping": { + "socialMediaAccounts": "#/components/schemas/socialMediaAccountDataInResponse" + } + }, + "x-abstract": true + }, + "errorLinks": { + "type": "object", + "properties": { + "about": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorObject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorLinks" + } + ], + "nullable": true + }, + "status": { + "type": "string" + }, + "code": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/errorSource" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { }, + "nullable": true + } + }, + "additionalProperties": false + }, + "errorResponseDocument": { + "required": [ + "errors", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorTopLevelLinks" + } + ] + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/errorObject" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "errorSource": { + "type": "object", + "properties": { + "pointer": { + "type": "string", + "nullable": true + }, + "parameter": { + "type": "string", + "nullable": true + }, + "header": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorTopLevelLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "resourceLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "resourceTopLevelLinks": { + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInPatchRequest": { + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "maxLength": 20, + "minLength": 2, + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "maxItems": 10, + "minItems": 1, + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInPostRequest": { + "required": [ + "lastName" + ], + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "maxLength": 20, + "minLength": 2, + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "maxItems": 10, + "minItems": 1, + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountAttributesInResponse": { + "type": "object", + "properties": { + "alternativeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "firstName": { + "maxLength": 20, + "minLength": 2, + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string" + }, + "userName": { + "maxLength": 18, + "minLength": 3, + "pattern": "^[a-zA-Z]+$", + "type": "string", + "nullable": true + }, + "creditCard": { + "type": "string", + "format": "credit-card", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "password": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "format": "tel", + "nullable": true + }, + "age": { + "maximum": 122.9, + "minimum": 0.1, + "type": "number", + "format": "double", + "nullable": true + }, + "profilePicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "backgroundPicture": { + "type": "string", + "format": "uri", + "nullable": true + }, + "tags": { + "maxItems": 10, + "minItems": 1, + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "countryCode": { + "type": "string", + "nullable": true + }, + "planet": { + "type": "string", + "nullable": true + }, + "nextRevalidation": { + "type": "string", + "format": "date-span", + "nullable": true + }, + "validatedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "validatedAtDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "validatedAtTime": { + "type": "string", + "format": "time", + "nullable": true + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/socialMediaAccountResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/socialMediaAccountResourceType" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountAttributesInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/resourceLinks" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + ], + "additionalProperties": false + }, + "socialMediaAccountPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "socialMediaAccountPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/resourceTopLevelLinks" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/socialMediaAccountDataInResponse" + } + ] + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "socialMediaAccountResourceType": { + "enum": [ + "socialMediaAccounts" + ], + "type": "string", + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiTests/ModelStateValidation/ModelStateValidationDbContext.cs b/test/OpenApiTests/ModelStateValidation/ModelStateValidationDbContext.cs new file mode 100644 index 0000000000..2823efdb46 --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/ModelStateValidationDbContext.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ModelStateValidation; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ModelStateValidationDbContext(DbContextOptions options) : TestableDbContext(options) +{ + public DbSet SocialMediaAccounts => Set(); +} diff --git a/test/OpenApiTests/ModelStateValidation/ModelStateValidationFakers.cs b/test/OpenApiTests/ModelStateValidation/ModelStateValidationFakers.cs new file mode 100644 index 0000000000..0427b52532 --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/ModelStateValidationFakers.cs @@ -0,0 +1,38 @@ +using Bogus; +using JetBrains.Annotations; +using TestBuildingBlocks; + +// @formatter:wrap_chained_method_calls chop_if_long +// @formatter:wrap_before_first_method_call true + +namespace OpenApiTests.ModelStateValidation; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ModelStateValidationFakers +{ + private readonly Lazy> _lazySocialMediaAccountFaker = new(() => new Faker() + .MakeDeterministic() + .RuleFor(socialMediaAccount => socialMediaAccount.AlternativeId, faker => faker.Random.Guid()) + .RuleFor(socialMediaAccount => socialMediaAccount.FirstName, faker => faker.Person.FirstName) + .RuleFor(socialMediaAccount => socialMediaAccount.LastName, faker => faker.Person.LastName) + .RuleFor(socialMediaAccount => socialMediaAccount.UserName, faker => faker.Random.String2(3, 18)) + .RuleFor(socialMediaAccount => socialMediaAccount.CreditCard, faker => faker.Finance.CreditCardNumber()) + .RuleFor(socialMediaAccount => socialMediaAccount.Email, faker => faker.Person.Email) + .RuleFor(socialMediaAccount => socialMediaAccount.Password, faker => Convert.ToBase64String(faker.Random.Bytes(faker.Random.Number(4, 75)))) + .RuleFor(socialMediaAccount => socialMediaAccount.Phone, faker => faker.Person.Phone) + .RuleFor(socialMediaAccount => socialMediaAccount.Age, faker => faker.Random.Double(0.1, 122.9)) + .RuleFor(socialMediaAccount => socialMediaAccount.ProfilePicture, faker => new Uri(faker.Image.LoremFlickrUrl())) + .RuleFor(socialMediaAccount => socialMediaAccount.BackgroundPicture, faker => faker.Image.LoremFlickrUrl()) + .RuleFor(socialMediaAccount => socialMediaAccount.Tags, faker => faker.Make(faker.Random.Number(1, 10), () => faker.Random.Word())) + .RuleFor(socialMediaAccount => socialMediaAccount.CountryCode, faker => faker.Random.ListItem([ + "NL", + "FR" + ])) + .RuleFor(socialMediaAccount => socialMediaAccount.Planet, faker => faker.Random.Word()) + .RuleFor(socialMediaAccount => socialMediaAccount.NextRevalidation, faker => TimeSpan.FromHours(faker.Random.Number(1, 5))) + .RuleFor(socialMediaAccount => socialMediaAccount.ValidatedAt, faker => faker.Date.Recent().ToUniversalTime().TruncateToWholeMilliseconds()) + .RuleFor(socialMediaAccount => socialMediaAccount.ValidatedAtDate, faker => DateOnly.FromDateTime(faker.Date.Recent())) + .RuleFor(socialMediaAccount => socialMediaAccount.ValidatedAtTime, faker => TimeOnly.FromDateTime(faker.Date.Recent().TruncateToWholeMilliseconds()))); + + public Faker SocialMediaAccount => _lazySocialMediaAccountFaker.Value; +} diff --git a/test/OpenApiTests/ModelStateValidation/ModelStateValidationTests.cs b/test/OpenApiTests/ModelStateValidation/ModelStateValidationTests.cs new file mode 100644 index 0000000000..e4d6ef04c2 --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/ModelStateValidationTests.cs @@ -0,0 +1,307 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ModelStateValidation; + +public sealed class ModelStateValidationTests : IClassFixture, ModelStateValidationDbContext>> +{ + // ReSharper disable once UseCollectionExpression (https://youtrack.jetbrains.com/issue/RSRP-497450) + public static readonly TheoryData SchemaNames = new() + { + "socialMediaAccountAttributesInPostRequest", + "socialMediaAccountAttributesInPatchRequest", + "socialMediaAccountAttributesInResponse" + }; + + private readonly OpenApiTestContext, ModelStateValidationDbContext> _testContext; + + public ModelStateValidationTests(OpenApiTestContext, ModelStateValidationDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + + const string targetFramework = +#if NET6_0 + "net6.0"; +#else + "net8.0"; +#endif + testContext.SwaggerDocumentOutputDirectory = $"{GetType().Namespace!.Replace('.', '/')}/GeneratedSwagger/{targetFramework}"; + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Guid_type_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.alternativeId").With(alternativeIdElement => + { + alternativeIdElement.Should().HaveProperty("type", "string"); + alternativeIdElement.Should().HaveProperty("format", "uuid"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Length_annotation_on_resource_string_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.firstName").With(firstNameElement => + { +#if !NET6_0 + firstNameElement.Should().HaveProperty("maxLength", 20); + firstNameElement.Should().HaveProperty("minLength", 2); +#endif + firstNameElement.Should().HaveProperty("type", "string"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Required_annotation_with_AllowEmptyStrings_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.lastName").With(lastNameElement => + { + lastNameElement.Should().NotContainPath("minLength"); + lastNameElement.Should().HaveProperty("type", "string"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task StringLength_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.userName").With(userNameElement => + { + userNameElement.Should().HaveProperty("maxLength", 18); + userNameElement.Should().HaveProperty("minLength", 3); + userNameElement.Should().HaveProperty("type", "string"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task RegularExpression_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.userName").With(userNameElement => + { + userNameElement.Should().HaveProperty("pattern", "^[a-zA-Z]+$"); + userNameElement.Should().HaveProperty("type", "string"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task CreditCard_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.creditCard").With(creditCardElement => + { + creditCardElement.Should().HaveProperty("type", "string"); + creditCardElement.Should().HaveProperty("format", "credit-card"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Email_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.email").With(emailElement => + { + emailElement.Should().HaveProperty("type", "string"); + emailElement.Should().HaveProperty("format", "email"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Min_max_length_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.password").With(passwordElement => + { +#if !NET6_0 + passwordElement.Should().HaveProperty("maxLength", 100); + passwordElement.Should().HaveProperty("minLength", 5); +#endif + passwordElement.Should().HaveProperty("type", "string"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Phone_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.phone").With(phoneElement => + { + phoneElement.Should().HaveProperty("type", "string"); + phoneElement.Should().HaveProperty("format", "tel"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Range_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.age").With(ageElement => + { + ageElement.Should().HaveProperty("maximum", 122.9); + ageElement.Should().NotContainPath("exclusiveMaximum"); + ageElement.Should().HaveProperty("minimum", 0.1); + ageElement.Should().NotContainPath("exclusiveMinimum"); + ageElement.Should().HaveProperty("type", "number"); + ageElement.Should().HaveProperty("format", "double"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Url_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.profilePicture").With(profilePictureElement => + { + profilePictureElement.Should().HaveProperty("type", "string"); + profilePictureElement.Should().HaveProperty("format", "uri"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Uri_type_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.backgroundPicture").With(backgroundPictureElement => + { + backgroundPictureElement.Should().HaveProperty("type", "string"); + backgroundPictureElement.Should().HaveProperty("format", "uri"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task Length_annotation_on_resource_list_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.tags").With(tagsElement => + { +#if !NET6_0 + tagsElement.Should().HaveProperty("maxItems", 10); + tagsElement.Should().HaveProperty("minItems", 1); +#endif + tagsElement.Should().HaveProperty("type", "array"); + + tagsElement.Should().ContainPath("items").With(itemsEl => + { + itemsEl.Should().HaveProperty("type", "string"); + }); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task TimeSpan_range_annotation_on_resource_property_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.nextRevalidation").With(nextRevalidationElement => + { + nextRevalidationElement.Should().HaveProperty("type", "string"); + nextRevalidationElement.Should().HaveProperty("format", "date-span"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task DateTime_type_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.validatedAt").With(validatedAtElement => + { + validatedAtElement.Should().HaveProperty("type", "string"); + validatedAtElement.Should().HaveProperty("format", "date-time"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task DateOnly_type_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.validatedAtDate").With(validatedDateAtElement => + { + validatedDateAtElement.Should().HaveProperty("type", "string"); + validatedDateAtElement.Should().HaveProperty("format", "date"); + }); + } + + [Theory] + [MemberData(nameof(SchemaNames))] + public async Task TimeOnly_type_produces_expected_schema(string modelName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{modelName}.properties.validatedAtTime").With(validatedTimeAtElement => + { + validatedTimeAtElement.Should().HaveProperty("type", "string"); + validatedTimeAtElement.Should().HaveProperty("format", "time"); + }); + } +} diff --git a/test/OpenApiTests/ModelStateValidation/SocialMediaAccount.cs b/test/OpenApiTests/ModelStateValidation/SocialMediaAccount.cs new file mode 100644 index 0000000000..1938618e0f --- /dev/null +++ b/test/OpenApiTests/ModelStateValidation/SocialMediaAccount.cs @@ -0,0 +1,96 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ModelStateValidation; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.ModelStateValidation", GenerateControllerEndpoints = JsonApiEndpoints.Post | JsonApiEndpoints.Patch)] +public sealed class SocialMediaAccount : Identifiable +{ + [Attr] + public Guid? AlternativeId { get; set; } + + [Attr] +#if !NET6_0 + [Length(2, 20)] +#endif + public string? FirstName { get; set; } + + [Attr] + [Required(AllowEmptyStrings = true)] + public string LastName { get; set; } = default!; + + [Attr] + [StringLength(18, MinimumLength = 3)] + [RegularExpression("^[a-zA-Z]+$", ErrorMessage = "Only letters are allowed.")] + public string? UserName { get; set; } + + [Attr] + [CreditCard] + public string? CreditCard { get; set; } + + [Attr] + [EmailAddress] + public string? Email { get; set; } + + [Attr] +#if !NET6_0 + [Base64String] + [MinLength(5)] + [MaxLength(100)] +#endif + public string? Password { get; set; } + + [Attr] + [Phone] + public string? Phone { get; set; } + + [Attr] +#if NET6_0 + [Range(0.1, 122.9)] +#else + [Range(0.1, 122.9, MinimumIsExclusive = true, MaximumIsExclusive = true)] +#endif + public double? Age { get; set; } + + [Attr] + public Uri? ProfilePicture { get; set; } + + [Attr] + [Url] + public string? BackgroundPicture { get; set; } + + [Attr] +#if !NET6_0 + [Length(1, 10)] +#endif + public List? Tags { get; set; } + + [Attr] +#if !NET6_0 + [AllowedValues(null, "NL", "FR")] +#endif + public string? CountryCode { get; set; } + + [Attr] +#if !NET6_0 + [DeniedValues("pluto")] +#endif + public string? Planet { get; set; } + + [Attr] + [Range(typeof(TimeSpan), "01:00", "05:00", ConvertValueInInvariantCulture = true)] + public TimeSpan? NextRevalidation { get; set; } + + [Attr] + public DateTime? ValidatedAt { get; set; } + + [Attr] + public DateOnly? ValidatedAtDate { get; set; } + + [Attr] + public TimeOnly? ValidatedAtTime { get; set; } +} diff --git a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs index e9688234b1..412148c26a 100644 --- a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs @@ -107,6 +107,11 @@ public void Be(object? value) _subject.ValueKind.Should().Be(JsonValueKind.Number); _subject.GetInt32().Should().Be(intValue); } + else if (value is double doubleValue) + { + _subject.ValueKind.Should().Be(JsonValueKind.Number); + _subject.GetDouble().Should().Be(doubleValue); + } else if (value is string stringValue) { _subject.ValueKind.Should().Be(JsonValueKind.String);