From 99dcfa802c51643ae1279a14068783bae547300d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 5 Sep 2022 15:46:09 +0200 Subject: [PATCH 01/66] Added tests for nullable and required properties in schema generation --- test/OpenApiTests/JsonElementExtensions.cs | 7 ++ .../ModelStateValidationDisabledStartup.cs | 19 +++++ .../NullableReferenceTypesDisabled/Chicken.cs | 34 +++++++++ .../ModelStateValidationDisabledTests.cs | 40 +++++++++++ .../ModelStateValidationEnabledTests.cs | 40 +++++++++++ .../NullabilityTests.cs | 62 ++++++++++++++++ ...NullableReferenceTypesDisabledDbContext.cs | 15 ++++ .../NullableReferenceTypesEnabled/Cow.cs | 39 ++++++++++ .../ModelStateValidationDisabledTests.cs | 42 +++++++++++ .../ModelStateValidationEnabledTests.cs | 42 +++++++++++ .../NullabilityTests.cs | 72 +++++++++++++++++++ .../NullableReferenceTypesEnabledDbContext.cs | 15 ++++ .../SchemaPropertiesStartup.cs | 18 +++++ 13 files changed, 445 insertions(+) create mode 100644 test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs create mode 100644 test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs diff --git a/test/OpenApiTests/JsonElementExtensions.cs b/test/OpenApiTests/JsonElementExtensions.cs index d8dc7d4e20..6f1ffe1432 100644 --- a/test/OpenApiTests/JsonElementExtensions.cs +++ b/test/OpenApiTests/JsonElementExtensions.cs @@ -19,6 +19,13 @@ public static JsonElement ShouldContainPath(this JsonElement source, string path return elementSelector.Should().NotThrow().Subject; } + public static void ShouldNotContainPath(this JsonElement source, string path) + { + JsonElement? pathToken = source.SelectToken(path); + + pathToken.Should().BeNull(); + } + public static void ShouldBeString(this JsonElement source, string value) { source.ValueKind.Should().Be(JsonValueKind.String); diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs new file mode 100644 index 0000000000..9895d7d61b --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.SchemaProperties; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public sealed class ModelStateValidationDisabledStartup : OpenApiStartup + where TDbContext : DbContext +{ + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + options.ValidateModelState = false; + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs new file mode 100644 index 0000000000..4865d33af9 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs @@ -0,0 +1,34 @@ +#nullable disable + +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class Chicken : Identifiable +{ + [Attr] + public string Name { get; set; } + + [Attr] + [Required] + public string NameOfCurrentFarm { get; set; } + + [Attr] + public int Age { get; set; } + + [Attr] + [Required] + public int Weight { get; set; } + + [Attr] + public int? TimeAtCurrentFarmInDays { get; set; } + + [Attr] + [Required] + public bool? HasProducedEggs { get; set; } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs new file mode 100644 index 0000000000..838e2a6bc2 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; + +public sealed class ModelStateValidationDisabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + testContext.UseController(); + } + + [Fact] + public async Task Resource_when_ModelStateValidation_is_disabled_produces_expected_required_property_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => + { + var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + requiredAttributes.ShouldNotBeNull(); + + requiredAttributes.Should().Contain("nameOfCurrentFarm"); + requiredAttributes.Should().Contain("weight"); + requiredAttributes.Should().Contain("hasProducedEggs"); + + requiredAttributes.ShouldHaveCount(3); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..b1c39ca090 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Resource_when_ModelStateValidation_is_enabled_produces_expected_required_property_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => + { + var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + requiredAttributes.ShouldNotBeNull(); + + requiredAttributes.Should().Contain("nameOfCurrentFarm"); + requiredAttributes.Should().Contain("weight"); + requiredAttributes.Should().Contain("hasProducedEggs"); + + requiredAttributes.ShouldHaveCount(3); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..e13be25d25 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public NullabilityTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled"; + } + + [Fact] + public async Task Resource_produces_expected_nullable_properties_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("name").With(propertyElement => + { + propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + + propertiesElement.ShouldContainPath("nameOfCurrentFarm").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("age").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("weight").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => + { + propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + + propertiesElement.ShouldContainPath("hasProducedEggs").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs new file mode 100644 index 0000000000..1208a369ef --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesDisabledDbContext : DbContext +{ + public DbSet Chicken => Set(); + + public NullableReferenceTypesDisabledDbContext(DbContextOptions options) + : base(options) + { + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs new file mode 100644 index 0000000000..3b5562a0b3 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class Cow : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [Attr] + [Required] + public string NameOfCurrentFarm { get; set; } = null!; + + [Attr] + public string? NameOfPreviousFarm { get; set; } + + [Attr] + [Required] + public string? Nickname { get; set; } + + [Attr] + public int Age { get; set; } + + [Attr] + [Required] + public int Weight { get; set; } + + [Attr] + public int? TimeAtCurrentFarmInDays { get; set; } + + [Attr] + [Required] + public bool? HasProducedMilk { get; set; } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs new file mode 100644 index 0000000000..f57c3bf4bc --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -0,0 +1,42 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; + +public sealed class ModelStateValidationDisabledTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Resource_when_ModelStateValidation_is_disabled_produces_expected_required_property_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => + { + var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + requiredAttributes.ShouldNotBeNull(); + + requiredAttributes.Should().Contain("nameOfCurrentFarm"); + requiredAttributes.Should().Contain("nickname"); + requiredAttributes.Should().Contain("weight"); + requiredAttributes.Should().Contain("hasProducedMilk"); + + requiredAttributes.ShouldHaveCount(4); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..7cb98fceda --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -0,0 +1,42 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Resource_when_ModelStateValidation_is_enabled_produces_expected_required_property_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => + { + var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + requiredAttributes.ShouldNotBeNull(); + + requiredAttributes.Should().Contain("name"); + requiredAttributes.Should().Contain("nameOfCurrentFarm"); + requiredAttributes.Should().Contain("nickname"); + requiredAttributes.Should().Contain("weight"); + requiredAttributes.Should().Contain("hasProducedMilk"); + + requiredAttributes.ShouldHaveCount(5); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..b69bb69d12 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -0,0 +1,72 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public NullabilityTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled"; + } + + [Fact] + public async Task Resource_produces_expected_nullable_properties_in_schema() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("name").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("nameOfCurrentFarm").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("nameOfPreviousFarm").With(propertyElement => + { + propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + + propertiesElement.ShouldContainPath("nickname").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("age").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("weight").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + + propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => + { + propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + + propertiesElement.ShouldContainPath("hasProducedMilk").With(propertyElement => + { + propertyElement.ShouldNotContainPath("nullable"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs new file mode 100644 index 0000000000..01c61aa589 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesEnabledDbContext : DbContext +{ + public DbSet Cow => Set(); + + public NullableReferenceTypesEnabledDbContext(DbContextOptions options) + : base(options) + { + } +} diff --git a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs new file mode 100644 index 0000000000..d4a1bf1d19 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.SchemaProperties; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public sealed class SchemaPropertiesStartup : OpenApiStartup + where TDbContext : DbContext +{ + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } +} From 2604f842e727972fbf022f27e05e569b63e524e7 Mon Sep 17 00:00:00 2001 From: maurei Date: Tue, 5 Jul 2022 19:17:22 +0200 Subject: [PATCH 02/66] Added handling of modelstate validation in setting required attributes --- .../ResourceFieldObjectSchemaBuilder.cs | 11 +++++++---- .../ResourceObjectSchemaGenerator.cs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index ef8c705424..c56054c858 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Reflection; -using System.Text.Json; +using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -23,24 +23,27 @@ internal sealed class ResourceFieldObjectSchemaBuilder private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; private readonly SchemaGenerator _defaultSchemaGenerator; private readonly ResourceTypeSchemaGenerator _resourceTypeSchemaGenerator; + private readonly IJsonApiOptions _options; private readonly SchemaRepository _resourceSchemaRepository = new(); private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator; private readonly IDictionary _schemasForResourceFields; public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, - SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, JsonNamingPolicy? namingPolicy) + SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, IJsonApiOptions options) { ArgumentGuard.NotNull(resourceTypeInfo, nameof(resourceTypeInfo)); ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); ArgumentGuard.NotNull(defaultSchemaGenerator, nameof(defaultSchemaGenerator)); ArgumentGuard.NotNull(resourceTypeSchemaGenerator, nameof(resourceTypeSchemaGenerator)); + ArgumentGuard.NotNull(options, nameof(options)); _resourceTypeInfo = resourceTypeInfo; _schemaRepositoryAccessor = schemaRepositoryAccessor; _defaultSchemaGenerator = defaultSchemaGenerator; _resourceTypeSchemaGenerator = resourceTypeSchemaGenerator; + _options = options; - _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, namingPolicy); + _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); _schemasForResourceFields = GetFieldSchemas(); } @@ -113,7 +116,7 @@ private bool IsFieldRequired(ResourceFieldAttribute field) return fieldTypeCategory switch { - TypeCategory.NonNullableReferenceType => true, + TypeCategory.NonNullableReferenceType => hasRequiredAttribute || _options.ValidateModelState, TypeCategory.ValueType => hasRequiredAttribute, TypeCategory.NullableReferenceType or TypeCategory.NullableValueType => hasRequiredAttribute, _ => throw new UnreachableCodeException() diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 616b98ef63..a4fda8d7d0 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -31,7 +31,7 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe _allowClientGeneratedIds = options.AllowClientGeneratedIds; _resourceFieldObjectSchemaBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, - defaultSchemaGenerator, _resourceTypeSchemaGenerator, options.SerializerOptions.PropertyNamingPolicy); + defaultSchemaGenerator, _resourceTypeSchemaGenerator, options); } public OpenApiSchema GenerateSchema(Type resourceObjectType) From f7194d94bafdd86c4831b3cd619b4cd5c238f00f Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 5 Sep 2022 15:50:13 +0200 Subject: [PATCH 03/66] Not all swagger documents need to be saved to disk; changes in OpenApiTestContext to allow for this --- test/OpenApiTests/OpenApiTestContext.cs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/test/OpenApiTests/OpenApiTestContext.cs b/test/OpenApiTests/OpenApiTestContext.cs index a750929827..9d4a95c33d 100644 --- a/test/OpenApiTests/OpenApiTestContext.cs +++ b/test/OpenApiTests/OpenApiTestContext.cs @@ -28,34 +28,27 @@ internal async Task GetSwaggerDocumentAsync() private async Task CreateSwaggerDocumentAsync() { - string absoluteOutputPath = GetSwaggerDocumentAbsoluteOutputPath(SwaggerDocumentOutputPath); - string content = await GetAsync("swagger/v1/swagger.json"); JsonElement rootElement = ParseSwaggerDocument(content); - await WriteToDiskAsync(absoluteOutputPath, rootElement); + + if (SwaggerDocumentOutputPath != null) + { + string absoluteOutputPath = GetSwaggerDocumentAbsoluteOutputPath(SwaggerDocumentOutputPath); + await WriteToDiskAsync(absoluteOutputPath, rootElement); + } return rootElement; } - private static string GetSwaggerDocumentAbsoluteOutputPath(string? relativePath) + private static string GetSwaggerDocumentAbsoluteOutputPath(string relativePath) { - AssertHasSwaggerDocumentOutputPath(relativePath); - string solutionRoot = Path.Combine(Assembly.GetExecutingAssembly().Location, "../../../../../../"); string outputPath = Path.Combine(solutionRoot, relativePath, "swagger.g.json"); return Path.GetFullPath(outputPath); } - private static void AssertHasSwaggerDocumentOutputPath([SysNotNull] string? relativePath) - { - if (relativePath is null) - { - throw new Exception($"Property '{nameof(OpenApiTestContext)}.{nameof(SwaggerDocumentOutputPath)}' must be set."); - } - } - private async Task GetAsync(string requestUrl) { using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); From 449a07985ae0041eedb82136e419272af5e51c51 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 6 Sep 2022 13:13:26 +0200 Subject: [PATCH 04/66] Added OpenApi client tests for nullable and required properties --- .editorconfig | 4 + .../{LegacyClient => }/ApiResponse.cs | 2 +- .../FakeHttpClientWrapper.cs | 2 +- .../MemberInfoExtensions.cs | 37 ++ .../OpenApiClientTests.csproj | 18 +- .../PropertyInfoAssertionsExtension.cs | 29 + .../NullableReferenceTypesDisabledClient.cs | 14 + .../NullabilityTests.cs | 27 + .../RequiredAttributesTests.cs | 114 ++++ .../swagger.g.json | 516 +++++++++++++++++ .../NullableReferenceTypesEnabledClient.cs | 14 + .../NullabilityTests.cs | 27 + .../RequiredAttributesTests.cs | 94 +++ .../swagger.g.json | 536 ++++++++++++++++++ test/OpenApiClientTests/TypeCategory.cs | 9 + 15 files changed, 1437 insertions(+), 6 deletions(-) rename test/OpenApiClientTests/{LegacyClient => }/ApiResponse.cs (94%) rename test/OpenApiClientTests/{LegacyClient => }/FakeHttpClientWrapper.cs (98%) create mode 100644 test/OpenApiClientTests/MemberInfoExtensions.cs create mode 100644 test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json create mode 100644 test/OpenApiClientTests/TypeCategory.cs diff --git a/.editorconfig b/.editorconfig index b6d9a8990c..70d678243d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,10 @@ insert_final_newline = true [*.{csproj,json}] indent_size = 2 +[test/OpenApiClientTests/obj/*.{cs}] +# Ignore compiler warnings triggered by code auto-generated by NSWag +dotnet_diagnostic.CS8625.severity = suggestion + [*.{cs}] #### .NET Coding Conventions #### diff --git a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs b/test/OpenApiClientTests/ApiResponse.cs similarity index 94% rename from test/OpenApiClientTests/LegacyClient/ApiResponse.cs rename to test/OpenApiClientTests/ApiResponse.cs index d009d2acee..0c353ea5c0 100644 --- a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs +++ b/test/OpenApiClientTests/ApiResponse.cs @@ -3,7 +3,7 @@ #pragma warning disable AV1008 // Class should not be static -namespace OpenApiClientTests.LegacyClient; +namespace OpenApiClientTests; internal static class ApiResponse { diff --git a/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs b/test/OpenApiClientTests/FakeHttpClientWrapper.cs similarity index 98% rename from test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs rename to test/OpenApiClientTests/FakeHttpClientWrapper.cs index 254f7f31cd..7240daf029 100644 --- a/test/OpenApiClientTests/LegacyClient/FakeHttpClientWrapper.cs +++ b/test/OpenApiClientTests/FakeHttpClientWrapper.cs @@ -3,7 +3,7 @@ using System.Text; using JsonApiDotNetCore.OpenApi.Client; -namespace OpenApiClientTests.LegacyClient; +namespace OpenApiClientTests; /// /// Enables to inject an outgoing response body and inspect the incoming request. diff --git a/test/OpenApiClientTests/MemberInfoExtensions.cs b/test/OpenApiClientTests/MemberInfoExtensions.cs new file mode 100644 index 0000000000..089570ccf2 --- /dev/null +++ b/test/OpenApiClientTests/MemberInfoExtensions.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using JsonApiDotNetCore.OpenApi.Client; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace OpenApiClientTests; + +internal static class MemberInfoExtensions +{ + public static TypeCategory GetTypeCategory(this MemberInfo source) + { + ArgumentGuard.NotNull(source, nameof(source)); + + Type memberType; + + if (source.MemberType.HasFlag(MemberTypes.Field)) + { + memberType = ((FieldInfo)source).FieldType; + } + else if (source.MemberType.HasFlag(MemberTypes.Property)) + { + memberType = ((PropertyInfo)source).PropertyType; + } + else + { + throw new NotSupportedException($"Member type '{source.MemberType}' must be a property or field."); + } + + if (memberType.IsValueType) + { + return Nullable.GetUnderlyingType(memberType) != null ? TypeCategory.NullableValueType : TypeCategory.ValueType; + } + + // Once we switch to .NET 6, we should rely instead on the built-in reflection APIs for nullability information. + // See https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-7/#libraries-reflection-apis-for-nullability-information. + return source.IsNonNullableReferenceType() ? TypeCategory.NonNullableReferenceType : TypeCategory.NullableReferenceType; + } +} diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 3f54c161b8..b16a6f3947 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -19,10 +19,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -62,5 +58,19 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions + + OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode + NullableReferenceTypesEnabledClient + NullableReferenceTypesEnabledClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + + + OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode + NullableReferenceTypesDisabledClient + NullableReferenceTypesDisabledClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false + \ No newline at end of file diff --git a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs new file mode 100644 index 0000000000..81992fd251 --- /dev/null +++ b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using FluentAssertions; +using FluentAssertions.Types; + +namespace OpenApiClientTests; + +internal static class PropertyInfoAssertionsExtensions +{ + [CustomAssertion] + public static void BeNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) + { + MemberInfo memberInfo = source.Subject; + + TypeCategory typeCategory = memberInfo.GetTypeCategory(); + + typeCategory.Should().Match(category => category == TypeCategory.NullableReferenceType || category == TypeCategory.NullableValueType, because, + becauseArgs); + } + + [CustomAssertion] + public static void BeNonNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) + { + MemberInfo memberInfo = source.Subject; + + TypeCategory typeCategory = memberInfo.GetTypeCategory(); + + typeCategory.Should().Match(category => category == TypeCategory.NonNullableReferenceType || category == TypeCategory.ValueType, because, becauseArgs); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs new file mode 100644 index 0000000000..c1a4eebab5 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; + +internal partial class NullableReferenceTypesDisabledClient : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..136c729537 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; + +public sealed class NullabilityTests +{ + [Fact] + public void Nullability_of_generated_types_is_as_expected() + { + PropertyInfo[] propertyInfos = typeof(ChickenAttributesInResponse).GetProperties(); + + PropertyInfo? propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.Name)); + propertyInfo.Should().BeNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.NameOfCurrentFarm)); + propertyInfo.Should().BeNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.Age)); + propertyInfo.Should().BeNonNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.TimeAtCurrentFarmInDays)); + propertyInfo.Should().BeNullable(); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs new file mode 100644 index 0000000000..3f6b203915 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs @@ -0,0 +1,114 @@ +using System.Net; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; +using NullableReferenceTypesDisabledClient = OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode.NullableReferenceTypesDisabledClient; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; + +public sealed class RequiredAttributesTests +{ + private const string HostPrefix = "http://localhost/"; + + [Fact] + public async Task Partial_posting_resource_with_explicitly_omitting_required_fields_produces_expected_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPostRequestDocument + { + Data = new ChickenDataInPostRequest + { + Attributes = new ChickenAttributesInPostRequest + { + HasProducedEggs = true + } + } + }; + + using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + chicken => chicken.HasProducedEggs)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(HostPrefix + "chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""chickens"", + ""attributes"": { + ""hasProducedEggs"": true + } + } +}"); + } + + [Fact] + public async Task Partial_posting_resource_without_explicitly_omitting_required_fields_fails() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPostRequestDocument + { + Data = new ChickenDataInPostRequest + { + Attributes = new ChickenAttributesInPostRequest + { + Weight = 3 + } + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); + } + + [Fact] + public async Task Patching_resource_with_missing_id_fails() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPatchRequestDocument + { + Data = new ChickenDataInPatchRequest + { + Attributes = new ChickenAttributesInPatchRequest + { + Age = 1 + } + } + }; + + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(1, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json new file mode 100644 index 0000000000..76771a1783 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json @@ -0,0 +1,516 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/chickens": { + "get": { + "tags": [ + "chickens" + ], + "operationId": "getChickenCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "chickens" + ], + "operationId": "headChickenCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "chickens" + ], + "operationId": "postChicken", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/chickens/{id}": { + "get": { + "tags": [ + "chickens" + ], + "operationId": "getChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "chickens" + ], + "operationId": "headChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "chickens" + ], + "operationId": "patchChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "chickens" + ], + "operationId": "deleteChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "components": { + "schemas": { + "chickenAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedEggs": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "chickenAttributesInPostRequest": { + "required": [ + "hasProducedEggs", + "nameOfCurrentFarm", + "weight" + ], + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedEggs": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "chickenAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedEggs": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "chickenCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chickenDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "chickenDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInPatchRequest" + } + }, + "additionalProperties": false + }, + "chickenDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInPostRequest" + } + }, + "additionalProperties": false + }, + "chickenDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "chickenPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "chickenPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInPostRequest" + } + }, + "additionalProperties": false + }, + "chickenPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "chickenResourceType": { + "enum": [ + "chickens" + ], + "type": "string" + }, + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs new file mode 100644 index 0000000000..17b822f56d --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; + +internal partial class NullableReferenceTypesEnabledClient : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..dfb1f0a934 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; + +public sealed class NullabilityTests +{ + [Fact] + public void Nullability_of_generated_types_is_as_expected() + { + PropertyInfo[] propertyInfos = typeof(CowAttributesInResponse).GetProperties(); + + PropertyInfo? propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.Name)); + propertyInfo.Should().BeNonNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.NameOfPreviousFarm)); + propertyInfo.Should().BeNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.Age)); + propertyInfo.Should().BeNonNullable(); + + propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.TimeAtCurrentFarmInDays)); + propertyInfo.Should().BeNullable(); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs new file mode 100644 index 0000000000..5f35ec7f29 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs @@ -0,0 +1,94 @@ +using System.Net; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; + +public sealed class RequiredAttributesTests +{ + private const string HostPrefix = "http://localhost/"; + + [Fact] + public async Task Partial_posting_resource_with_explicitly_omitting_required_fields_produces_expected_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPostRequestDocument + { + Data = new CowDataInPostRequest + { + Attributes = new CowAttributesInPostRequest + { + HasProducedMilk = true, + Weight = 1100 + } + } + }; + + using (apiClient.RegisterAttributesForRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(HostPrefix + "cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cows"", + ""attributes"": { + ""weight"": 1100, + ""hasProducedMilk"": true + } + } +}"); + } + + [Fact] + public async Task Partial_posting_resource_without_explicitly_omitting_required_fields_produces_expected_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPostRequestDocument + { + Data = new CowDataInPostRequest + { + Attributes = new CowAttributesInPostRequest + { + Weight = 1100, + Age = 5, + Name = "Cow 1", + NameOfCurrentFarm = "123", + NameOfPreviousFarm = "123" + } + } + }; + + // Act + Func> + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'nickname'. Property requires a value. Path 'data.attributes'."); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json new file mode 100644 index 0000000000..78e6dc1a73 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json @@ -0,0 +1,536 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/cows": { + "get": { + "tags": [ + "cows" + ], + "operationId": "getCowCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cows" + ], + "operationId": "headCowCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "cows" + ], + "operationId": "postCow", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/cows/{id}": { + "get": { + "tags": [ + "cows" + ], + "operationId": "getCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cows" + ], + "operationId": "headCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cows" + ], + "operationId": "patchCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "cows" + ], + "operationId": "deleteCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "components": { + "schemas": { + "cowAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "nameOfPreviousFarm": { + "type": "string", + "nullable": true + }, + "nickname": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedMilk": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "cowAttributesInPostRequest": { + "required": [ + "hasProducedMilk", + "name", + "nameOfCurrentFarm", + "nickname", + "weight" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "nameOfPreviousFarm": { + "type": "string", + "nullable": true + }, + "nickname": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedMilk": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "cowAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nameOfCurrentFarm": { + "type": "string" + }, + "nameOfPreviousFarm": { + "type": "string", + "nullable": true + }, + "nickname": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedMilk": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "cowCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/cowDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "cowDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInPatchRequest" + } + }, + "additionalProperties": false + }, + "cowDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInPostRequest" + } + }, + "additionalProperties": false + }, + "cowDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "cowPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "cowPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInPostRequest" + } + }, + "additionalProperties": false + }, + "cowPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "cowResourceType": { + "enum": [ + "cows" + ], + "type": "string" + }, + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/TypeCategory.cs b/test/OpenApiClientTests/TypeCategory.cs new file mode 100644 index 0000000000..301e37c8f6 --- /dev/null +++ b/test/OpenApiClientTests/TypeCategory.cs @@ -0,0 +1,9 @@ +namespace OpenApiClientTests; + +internal enum TypeCategory +{ + NonNullableReferenceType, + NullableReferenceType, + ValueType, + NullableValueType +} From c55a2cba22f850bddf099d9600457d5b543b399e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 6 Sep 2022 13:58:06 +0200 Subject: [PATCH 05/66] Use NullabilityInfoContext for nullability information --- .../MemberInfoExtensions.cs | 36 ----------------- .../ResourceFieldAttributeExtensions.cs | 31 ++++++++++++--- .../JsonApiSchemaGenerator.cs | 22 +++++------ .../ResourceFieldObjectSchemaBuilder.cs | 39 ++++++++++--------- src/JsonApiDotNetCore.OpenApi/TypeCategory.cs | 9 ----- .../MemberInfoExtensions.cs | 37 ------------------ .../PropertyInfoAssertionsExtension.cs | 15 +++---- .../RequiredAttributesTests.cs | 1 - test/OpenApiClientTests/TypeCategory.cs | 9 ----- 9 files changed, 65 insertions(+), 134 deletions(-) delete mode 100644 src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs delete mode 100644 src/JsonApiDotNetCore.OpenApi/TypeCategory.cs delete mode 100644 test/OpenApiClientTests/MemberInfoExtensions.cs delete mode 100644 test/OpenApiClientTests/TypeCategory.cs diff --git a/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs b/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs deleted file mode 100644 index a6f46ccb9d..0000000000 --- a/src/JsonApiDotNetCore.OpenApi/MemberInfoExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JsonApiDotNetCore.OpenApi; - -internal static class MemberInfoExtensions -{ - public static TypeCategory GetTypeCategory(this MemberInfo source) - { - ArgumentGuard.NotNull(source, nameof(source)); - - Type memberType; - - if (source.MemberType.HasFlag(MemberTypes.Field)) - { - memberType = ((FieldInfo)source).FieldType; - } - else if (source.MemberType.HasFlag(MemberTypes.Property)) - { - memberType = ((PropertyInfo)source).PropertyType; - } - else - { - throw new NotSupportedException($"Member type '{source.MemberType}' must be a property or field."); - } - - if (memberType.IsValueType) - { - return Nullable.GetUnderlyingType(memberType) != null ? TypeCategory.NullableValueType : TypeCategory.ValueType; - } - - // Once we switch to .NET 6, we should rely instead on the built-in reflection APIs for nullability information. - // See https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-7/#libraries-reflection-apis-for-nullability-information. - return source.IsNonNullableReferenceType() ? TypeCategory.NonNullableReferenceType : TypeCategory.NullableReferenceType; - } -} diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs index 32bf9f5187..d4b9c40fea 100644 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Reflection; using JsonApiDotNetCore.Resources.Annotations; using Swashbuckle.AspNetCore.SwaggerGen; @@ -6,16 +7,34 @@ namespace JsonApiDotNetCore.OpenApi; internal static class ResourceFieldAttributeExtensions { + private static readonly NullabilityInfoContext NullabilityInfoContext = new(); + public static bool IsNullable(this ResourceFieldAttribute source) { - TypeCategory fieldTypeCategory = source.Property.GetTypeCategory(); bool hasRequiredAttribute = source.Property.HasAttribute(); - return fieldTypeCategory switch + if (hasRequiredAttribute) { - TypeCategory.NonNullableReferenceType or TypeCategory.ValueType => false, - TypeCategory.NullableReferenceType or TypeCategory.NullableValueType => !hasRequiredAttribute, - _ => throw new UnreachableCodeException() - }; + // Reflects the following cases, independent of NRT setting + // `[Required] int? Number` => not nullable + // `[Required] int Number` => not nullable + // `[Required] string Text` => not nullable + // `[Required] string? Text` => not nullable + // `[Required] string Text` => not nullable + return false; + } + + NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(source.Property); + + // Reflects the following cases: + // Independent of NRT: + // `int? Number` => nullable + // `int Number` => not nullable + // If NRT is enabled: + // `string? Text` => nullable + // `string Text` => not nullable + // If NRT is disabled: + // `string Text` => nullable + return nullabilityInfo.ReadState is not NullabilityState.NotNull; } } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs index a0e7159a89..c5fe2b6a2d 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs @@ -26,6 +26,13 @@ internal sealed class JsonApiSchemaGenerator : ISchemaGenerator typeof(NullableToOneRelationshipInRequest<>) }; + private static readonly Type[] JsonApiDocumentWithNullableDataOpenTypes = + { + typeof(NullableSecondaryResourceResponseDocument<>), + typeof(NullableResourceIdentifierResponseDocument<>), + typeof(NullableToOneRelationshipInRequest<>) + }; + private readonly ISchemaGenerator _defaultSchemaGenerator; private readonly ResourceObjectSchemaGenerator _resourceObjectSchemaGenerator; private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator; @@ -58,7 +65,7 @@ public OpenApiSchema GenerateSchema(Type type, SchemaRepository schemaRepository { OpenApiSchema schema = GenerateJsonApiDocumentSchema(type); - if (IsDataPropertyNullable(type)) + if (IsDataPropertyNullableInDocument(type)) { SetDataObjectSchemaToNullable(schema); } @@ -98,18 +105,11 @@ private static bool IsManyDataDocument(Type documentType) return documentType.BaseType!.GetGenericTypeDefinition() == typeof(ManyData<>); } - private static bool IsDataPropertyNullable(Type type) + private static bool IsDataPropertyNullableInDocument(Type documentType) { - PropertyInfo? dataProperty = type.GetProperty(nameof(JsonApiObjectPropertyName.Data)); - - if (dataProperty == null) - { - throw new UnreachableCodeException(); - } - - TypeCategory typeCategory = dataProperty.GetTypeCategory(); + Type documentOpenType = documentType.GetGenericTypeDefinition(); - return typeCategory == TypeCategory.NullableReferenceType; + return JsonApiDocumentWithNullableDataOpenTypes.Contains(documentOpenType); } private void SetDataObjectSchemaToNullable(OpenApiSchema referenceSchemaForDocument) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index c56054c858..6dbf35dd1e 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -12,13 +12,21 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class ResourceFieldObjectSchemaBuilder { - private static readonly Type[] RelationshipInResponseOpenTypes = + private static readonly NullabilityInfoContext NullabilityInfoContext = new(); + + private static readonly Type[] RelationshipSchemaInResponseOpenTypes = { typeof(ToOneRelationshipInResponse<>), typeof(ToManyRelationshipInResponse<>), typeof(NullableToOneRelationshipInResponse<>) }; + private static readonly Type[] NullableRelationshipSchemaOpenTypes = + { + typeof(NullableToOneRelationshipInRequest<>), + typeof(NullableToOneRelationshipInResponse<>) + }; + private readonly ResourceTypeInfo _resourceTypeInfo; private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; private readonly SchemaGenerator _defaultSchemaGenerator; @@ -111,15 +119,14 @@ private bool IsFieldRequired(ResourceFieldAttribute field) return false; } - TypeCategory fieldTypeCategory = field.Property.GetTypeCategory(); bool hasRequiredAttribute = field.Property.HasAttribute(); - return fieldTypeCategory switch + NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(field.Property); + + return field.Property.PropertyType.IsValueType switch { - TypeCategory.NonNullableReferenceType => hasRequiredAttribute || _options.ValidateModelState, - TypeCategory.ValueType => hasRequiredAttribute, - TypeCategory.NullableReferenceType or TypeCategory.NullableValueType => hasRequiredAttribute, - _ => throw new UnreachableCodeException() + true => hasRequiredAttribute, + false => _options.ValidateModelState ? nullabilityInfo.ReadState == NullabilityState.NotNull || hasRequiredAttribute : hasRequiredAttribute }; } @@ -197,7 +204,9 @@ private OpenApiSchema CreateRelationshipSchema(Type relationshipSchemaType) OpenApiSchema fullSchema = _schemaRepositoryAccessor.Current.Schemas[referenceSchema.Reference.Id]; - if (IsDataPropertyNullable(relationshipSchemaType)) + Console.WriteLine(relationshipSchemaType.FullName); + + if (IsDataPropertyNullableInRelationshipSchemaType(relationshipSchemaType)) { fullSchema.Properties[JsonApiObjectPropertyName.Data] = _nullableReferenceSchemaGenerator.GenerateSchema(fullSchema.Properties[JsonApiObjectPropertyName.Data]); @@ -215,18 +224,12 @@ private static bool IsRelationshipInResponseType(Type relationshipSchemaType) { Type relationshipSchemaOpenType = relationshipSchemaType.GetGenericTypeDefinition(); - return RelationshipInResponseOpenTypes.Contains(relationshipSchemaOpenType); + return RelationshipSchemaInResponseOpenTypes.Contains(relationshipSchemaOpenType); } - private static bool IsDataPropertyNullable(Type type) + private static bool IsDataPropertyNullableInRelationshipSchemaType(Type relationshipSchemaType) { - PropertyInfo? dataProperty = type.GetProperty(nameof(JsonApiObjectPropertyName.Data)); - - if (dataProperty == null) - { - throw new UnreachableCodeException(); - } - - return dataProperty.GetTypeCategory() == TypeCategory.NullableReferenceType; + Type relationshipSchemaOpenType = relationshipSchemaType.GetGenericTypeDefinition(); + return NullableRelationshipSchemaOpenTypes.Contains(relationshipSchemaOpenType); } } diff --git a/src/JsonApiDotNetCore.OpenApi/TypeCategory.cs b/src/JsonApiDotNetCore.OpenApi/TypeCategory.cs deleted file mode 100644 index 1641e31775..0000000000 --- a/src/JsonApiDotNetCore.OpenApi/TypeCategory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace JsonApiDotNetCore.OpenApi; - -internal enum TypeCategory -{ - NonNullableReferenceType, - NullableReferenceType, - ValueType, - NullableValueType -} diff --git a/test/OpenApiClientTests/MemberInfoExtensions.cs b/test/OpenApiClientTests/MemberInfoExtensions.cs deleted file mode 100644 index 089570ccf2..0000000000 --- a/test/OpenApiClientTests/MemberInfoExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Reflection; -using JsonApiDotNetCore.OpenApi.Client; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace OpenApiClientTests; - -internal static class MemberInfoExtensions -{ - public static TypeCategory GetTypeCategory(this MemberInfo source) - { - ArgumentGuard.NotNull(source, nameof(source)); - - Type memberType; - - if (source.MemberType.HasFlag(MemberTypes.Field)) - { - memberType = ((FieldInfo)source).FieldType; - } - else if (source.MemberType.HasFlag(MemberTypes.Property)) - { - memberType = ((PropertyInfo)source).PropertyType; - } - else - { - throw new NotSupportedException($"Member type '{source.MemberType}' must be a property or field."); - } - - if (memberType.IsValueType) - { - return Nullable.GetUnderlyingType(memberType) != null ? TypeCategory.NullableValueType : TypeCategory.ValueType; - } - - // Once we switch to .NET 6, we should rely instead on the built-in reflection APIs for nullability information. - // See https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-7/#libraries-reflection-apis-for-nullability-information. - return source.IsNonNullableReferenceType() ? TypeCategory.NonNullableReferenceType : TypeCategory.NullableReferenceType; - } -} diff --git a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs index 81992fd251..c3338a300b 100644 --- a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs +++ b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs @@ -6,24 +6,25 @@ namespace OpenApiClientTests; internal static class PropertyInfoAssertionsExtensions { + private static readonly NullabilityInfoContext NullabilityInfoContext = new(); + [CustomAssertion] public static void BeNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) { - MemberInfo memberInfo = source.Subject; + PropertyInfo propertyInfo = source.Subject; - TypeCategory typeCategory = memberInfo.GetTypeCategory(); + NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); - typeCategory.Should().Match(category => category == TypeCategory.NullableReferenceType || category == TypeCategory.NullableValueType, because, - becauseArgs); + nullabilityInfo.ReadState.Should().NotBe(NullabilityState.NotNull, because, becauseArgs); } [CustomAssertion] public static void BeNonNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) { - MemberInfo memberInfo = source.Subject; + PropertyInfo propertyInfo = source.Subject; - TypeCategory typeCategory = memberInfo.GetTypeCategory(); + NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); - typeCategory.Should().Match(category => category == TypeCategory.NonNullableReferenceType || category == TypeCategory.ValueType, because, becauseArgs); + nullabilityInfo.ReadState.Should().Be(NullabilityState.NotNull, because, becauseArgs); } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs index 3f6b203915..b97535f908 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs @@ -7,7 +7,6 @@ using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; using TestBuildingBlocks; using Xunit; -using NullableReferenceTypesDisabledClient = OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode.NullableReferenceTypesDisabledClient; namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; diff --git a/test/OpenApiClientTests/TypeCategory.cs b/test/OpenApiClientTests/TypeCategory.cs deleted file mode 100644 index 301e37c8f6..0000000000 --- a/test/OpenApiClientTests/TypeCategory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace OpenApiClientTests; - -internal enum TypeCategory -{ - NonNullableReferenceType, - NullableReferenceType, - ValueType, - NullableValueType -} From fc159125a15d5206f697a2b5e06c6b9ee87857f1 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 8 Sep 2022 02:31:45 +0200 Subject: [PATCH 06/66] Post-merge fixes --- .../swagger.g.json | 17 ++++++++++++---- .../swagger.g.json | 20 +++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json index 76771a1783..d71423c997 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json @@ -58,7 +58,7 @@ }, "responses": { "201": { - "description": "Success", + "description": "Created", "content": { "application/vnd.api+json": { "schema": { @@ -68,7 +68,7 @@ } }, "204": { - "description": "Success" + "description": "No Content" } } } @@ -169,7 +169,7 @@ } }, "204": { - "description": "Success" + "description": "No Content" } } }, @@ -191,7 +191,7 @@ ], "responses": { "204": { - "description": "Success" + "description": "No Content" } } } @@ -207,6 +207,7 @@ "nullable": true }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "age": { @@ -241,6 +242,7 @@ "nullable": true }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "age": { @@ -270,6 +272,7 @@ "nullable": true }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "age": { @@ -328,6 +331,7 @@ "$ref": "#/components/schemas/chickenResourceType" }, "id": { + "minLength": 1, "type": "string" }, "attributes": { @@ -363,6 +367,7 @@ "$ref": "#/components/schemas/chickenResourceType" }, "id": { + "minLength": 1, "type": "string" }, "attributes": { @@ -464,12 +469,14 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" }, "describedby": { "type": "string" }, "first": { + "minLength": 1, "type": "string" }, "last": { @@ -491,6 +498,7 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" }, "describedby": { @@ -506,6 +514,7 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" } }, diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json index 78e6dc1a73..14669b5c84 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json @@ -58,7 +58,7 @@ }, "responses": { "201": { - "description": "Success", + "description": "Created", "content": { "application/vnd.api+json": { "schema": { @@ -68,7 +68,7 @@ } }, "204": { - "description": "Success" + "description": "No Content" } } } @@ -169,7 +169,7 @@ } }, "204": { - "description": "Success" + "description": "No Content" } } }, @@ -191,7 +191,7 @@ ], "responses": { "204": { - "description": "Success" + "description": "No Content" } } } @@ -206,6 +206,7 @@ "type": "string" }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "nameOfPreviousFarm": { @@ -213,6 +214,7 @@ "nullable": true }, "nickname": { + "minLength": 1, "type": "string" }, "age": { @@ -248,6 +250,7 @@ "type": "string" }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "nameOfPreviousFarm": { @@ -255,6 +258,7 @@ "nullable": true }, "nickname": { + "minLength": 1, "type": "string" }, "age": { @@ -283,6 +287,7 @@ "type": "string" }, "nameOfCurrentFarm": { + "minLength": 1, "type": "string" }, "nameOfPreviousFarm": { @@ -290,6 +295,7 @@ "nullable": true }, "nickname": { + "minLength": 1, "type": "string" }, "age": { @@ -348,6 +354,7 @@ "$ref": "#/components/schemas/cowResourceType" }, "id": { + "minLength": 1, "type": "string" }, "attributes": { @@ -383,6 +390,7 @@ "$ref": "#/components/schemas/cowResourceType" }, "id": { + "minLength": 1, "type": "string" }, "attributes": { @@ -484,12 +492,14 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" }, "describedby": { "type": "string" }, "first": { + "minLength": 1, "type": "string" }, "last": { @@ -511,6 +521,7 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" }, "describedby": { @@ -526,6 +537,7 @@ "type": "object", "properties": { "self": { + "minLength": 1, "type": "string" } }, From d7c8b4b04190cec2b900be40bd68e5c0746e272a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:40:56 +0100 Subject: [PATCH 07/66] Post-merge fixes --- .../SchemaProperties/ModelStateValidationDisabledStartup.cs | 4 ++-- .../NullableReferenceTypesDisabledDbContext.cs | 3 ++- .../NullableReferenceTypesEnabledDbContext.cs | 3 ++- test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs index 9895d7d61b..b2906ba460 100644 --- a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs @@ -1,13 +1,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class ModelStateValidationDisabledStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs index 1208a369ef..16bdc07e15 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : DbContext +public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext { public DbSet Chicken => Set(); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs index 01c61aa589..b7011e7d27 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -1,10 +1,11 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : DbContext +public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext { public DbSet Cow => Set(); diff --git a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs index d4a1bf1d19..03f6531631 100644 --- a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs +++ b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs @@ -1,13 +1,13 @@ using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class SchemaPropertiesStartup : OpenApiStartup - where TDbContext : DbContext + where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) { From d72e43ee93cfe2f973086dc04925fecc4d5b6c6b Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:37:21 +0100 Subject: [PATCH 08/66] Fixed: do not share NullabilityInfoContext, it is not thread-safe --- .../ResourceFieldAttributeExtensions.cs | 5 ++--- .../SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs | 5 ++--- .../OpenApiClientTests/PropertyInfoAssertionsExtension.cs | 8 ++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs index d4b9c40fea..b1274cbe97 100644 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs @@ -7,8 +7,6 @@ namespace JsonApiDotNetCore.OpenApi; internal static class ResourceFieldAttributeExtensions { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - public static bool IsNullable(this ResourceFieldAttribute source) { bool hasRequiredAttribute = source.Property.HasAttribute(); @@ -24,7 +22,8 @@ public static bool IsNullable(this ResourceFieldAttribute source) return false; } - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(source.Property); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(source.Property); // Reflects the following cases: // Independent of NRT: diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 9d068f3dc9..804fae41fc 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class ResourceFieldObjectSchemaBuilder { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - private static readonly Type[] RelationshipSchemaInResponseOpenTypes = { typeof(ToOneRelationshipInResponse<>), @@ -121,7 +119,8 @@ private bool IsFieldRequired(ResourceFieldAttribute field) bool hasRequiredAttribute = field.Property.HasAttribute(); - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(field.Property); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(field.Property); return field.Property.PropertyType.IsValueType switch { diff --git a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs index c3338a300b..b29c635f8e 100644 --- a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs +++ b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs @@ -6,14 +6,13 @@ namespace OpenApiClientTests; internal static class PropertyInfoAssertionsExtensions { - private static readonly NullabilityInfoContext NullabilityInfoContext = new(); - [CustomAssertion] public static void BeNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) { PropertyInfo propertyInfo = source.Subject; - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); nullabilityInfo.ReadState.Should().NotBe(NullabilityState.NotNull, because, becauseArgs); } @@ -23,7 +22,8 @@ public static void BeNonNullable(this PropertyInfoAssertions source, string beca { PropertyInfo propertyInfo = source.Subject; - NullabilityInfo nullabilityInfo = NullabilityInfoContext.Create(propertyInfo); + NullabilityInfoContext nullabilityContext = new(); + NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); nullabilityInfo.ReadState.Should().Be(NullabilityState.NotNull, because, becauseArgs); } From f8e640bd29668e7039d546efb3b0a431fad7a2da Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 19 Dec 2022 09:13:10 +0100 Subject: [PATCH 09/66] Review feedback OpenApiTests/SchemaProperties test collection: * Allow for usage of OpenApiStartup directly * Remove unneeded adding of JsonConverter * Replace null checks with .ShouldHaveCount() calls * Adjust test names Misc: * Reverted .editorconfig changes and fixed ApiException constructor instead * Remove Debug statement --- .editorconfig | 4 ---- .../ApiException.cs | 2 +- .../ResourceFieldObjectSchemaBuilder.cs | 2 -- test/OpenApiTests/OpenApiStartup.cs | 3 ++- .../ModelStateValidationDisabledStartup.cs | 3 +-- .../ModelStateValidationDisabledTests.cs | 8 ++++---- .../ModelStateValidationEnabledTests.cs | 13 ++++++------- .../NullabilityTests.cs | 14 +++++++------- .../ModelStateValidationDisabledTests.cs | 7 +++---- .../ModelStateValidationEnabledTests.cs | 13 ++++++------- .../NullabilityTests.cs | 14 +++++++------- .../SchemaPropertiesStartup.cs | 18 ------------------ 12 files changed, 37 insertions(+), 64 deletions(-) delete mode 100644 test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs diff --git a/.editorconfig b/.editorconfig index 7e3b5e1cf2..ca191cf90e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,10 +11,6 @@ insert_final_newline = true [*.{config,csproj,css,js,json,props,ruleset,xslt}] indent_size = 2 -[test/OpenApiClientTests/obj/*.{cs}] -# Ignore compiler warnings triggered by code auto-generated by NSWag -dotnet_diagnostic.CS8625.severity = suggestion - [*.{cs}] #### C#/.NET Coding Conventions #### diff --git a/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs b/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs index 1224308af9..cc7cdc8104 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs @@ -15,7 +15,7 @@ public sealed class ApiException : Exception public IReadOnlyDictionary> Headers { get; } - public ApiException(string message, int statusCode, string? response, IReadOnlyDictionary> headers, Exception innerException) + public ApiException(string message, int statusCode, string? response, IReadOnlyDictionary> headers, Exception? innerException) : base($"{message}\n\nStatus: {statusCode}\nResponse: \n{response ?? "(null)"}", innerException) { StatusCode = statusCode; diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 804fae41fc..e4abf42549 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -203,8 +203,6 @@ private OpenApiSchema CreateRelationshipSchema(Type relationshipSchemaType) OpenApiSchema fullSchema = _schemaRepositoryAccessor.Current.Schemas[referenceSchema.Reference.Id]; - Console.WriteLine(relationshipSchemaType.FullName); - if (IsDataPropertyNullableInRelationshipSchemaType(relationshipSchemaType)) { fullSchema.Properties[JsonApiObjectPropertyName.Data] = diff --git a/test/OpenApiTests/OpenApiStartup.cs b/test/OpenApiTests/OpenApiStartup.cs index 46978e7f67..a1a46e4022 100644 --- a/test/OpenApiTests/OpenApiStartup.cs +++ b/test/OpenApiTests/OpenApiStartup.cs @@ -6,7 +6,7 @@ namespace OpenApiTests; -public abstract class OpenApiStartup : TestableStartup +public class OpenApiStartup : TestableStartup where TDbContext : TestableDbContext { public override void ConfigureServices(IServiceCollection services) @@ -26,3 +26,4 @@ public override void Configure(IApplicationBuilder app) app.UseEndpoints(endpoints => endpoints.MapControllers()); } } + diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs index b2906ba460..ba9ea319d0 100644 --- a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using TestBuildingBlocks; @@ -13,7 +12,7 @@ protected override void SetJsonApiOptions(JsonApiOptions options) { base.SetJsonApiOptions(options); - options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.ValidateModelState = false; } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs index 838e2a6bc2..209423df20 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -15,11 +15,12 @@ public ModelStateValidationDisabledTests( OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) { _testContext = testContext; + testContext.UseController(); } [Fact] - public async Task Resource_when_ModelStateValidation_is_disabled_produces_expected_required_property_in_schema() + public async Task Produces_expected_required_property_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -28,13 +29,12 @@ public async Task Resource_when_ModelStateValidation_is_disabled_produces_expect document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => { var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); - requiredAttributes.ShouldNotBeNull(); + requiredAttributes.ShouldHaveCount(3); requiredAttributes.Should().Contain("nameOfCurrentFarm"); requiredAttributes.Should().Contain("weight"); requiredAttributes.Should().Contain("hasProducedEggs"); - - requiredAttributes.ShouldHaveCount(3); }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs index b1c39ca090..b028eb9cfa 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -6,12 +6,12 @@ namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> + : IClassFixture, NullableReferenceTypesDisabledDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) { _testContext = testContext; @@ -19,7 +19,7 @@ public ModelStateValidationEnabledTests( } [Fact] - public async Task Resource_when_ModelStateValidation_is_enabled_produces_expected_required_property_in_schema() + public async Task Produces_expected_required_property_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -28,13 +28,12 @@ public async Task Resource_when_ModelStateValidation_is_enabled_produces_expecte document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => { var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); - requiredAttributes.ShouldNotBeNull(); + requiredAttributes.ShouldHaveCount(3); requiredAttributes.Should().Contain("nameOfCurrentFarm"); requiredAttributes.Should().Contain("weight"); requiredAttributes.Should().Contain("hasProducedEggs"); - - requiredAttributes.ShouldHaveCount(3); }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index e13be25d25..4e8b216dbe 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -6,12 +6,11 @@ namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> + : IClassFixture, NullableReferenceTypesDisabledDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) { _testContext = testContext; @@ -20,7 +19,7 @@ public NullabilityTests( } [Fact] - public async Task Resource_produces_expected_nullable_properties_in_schema() + public async Task Produces_expected_nullable_properties_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -30,7 +29,7 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() { propertiesElement.ShouldContainPath("name").With(propertyElement => { - propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); propertiesElement.ShouldContainPath("nameOfCurrentFarm").With(propertyElement => @@ -50,7 +49,7 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => { - propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); propertiesElement.ShouldContainPath("hasProducedEggs").With(propertyElement => @@ -60,3 +59,4 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs index f57c3bf4bc..06083f643c 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -20,7 +20,7 @@ public ModelStateValidationDisabledTests( } [Fact] - public async Task Resource_when_ModelStateValidation_is_disabled_produces_expected_required_property_in_schema() + public async Task Produces_expected_required_property_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -29,14 +29,13 @@ public async Task Resource_when_ModelStateValidation_is_disabled_produces_expect document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => { var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); - requiredAttributes.ShouldNotBeNull(); + requiredAttributes.ShouldHaveCount(4); requiredAttributes.Should().Contain("nameOfCurrentFarm"); requiredAttributes.Should().Contain("nickname"); requiredAttributes.Should().Contain("weight"); requiredAttributes.Should().Contain("hasProducedMilk"); - - requiredAttributes.ShouldHaveCount(4); }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs index 7cb98fceda..034d49be49 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -6,12 +6,12 @@ namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> + : IClassFixture, NullableReferenceTypesEnabledDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) { _testContext = testContext; @@ -19,7 +19,7 @@ public ModelStateValidationEnabledTests( } [Fact] - public async Task Resource_when_ModelStateValidation_is_enabled_produces_expected_required_property_in_schema() + public async Task Produces_expected_required_property_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -28,15 +28,14 @@ public async Task Resource_when_ModelStateValidation_is_enabled_produces_expecte document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => { var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); - requiredAttributes.ShouldNotBeNull(); + requiredAttributes.ShouldHaveCount(5); requiredAttributes.Should().Contain("name"); requiredAttributes.Should().Contain("nameOfCurrentFarm"); requiredAttributes.Should().Contain("nickname"); requiredAttributes.Should().Contain("weight"); requiredAttributes.Should().Contain("hasProducedMilk"); - - requiredAttributes.ShouldHaveCount(5); }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index b69bb69d12..d9a7f4b6eb 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -6,12 +6,11 @@ namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> + : IClassFixture, NullableReferenceTypesEnabledDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) { _testContext = testContext; @@ -20,7 +19,7 @@ public NullabilityTests( } [Fact] - public async Task Resource_produces_expected_nullable_properties_in_schema() + public async Task Produces_expected_nullable_properties_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -40,7 +39,7 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() propertiesElement.ShouldContainPath("nameOfPreviousFarm").With(propertyElement => { - propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); propertiesElement.ShouldContainPath("nickname").With(propertyElement => @@ -60,7 +59,7 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => { - propertyElement.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); propertiesElement.ShouldContainPath("hasProducedMilk").With(propertyElement => @@ -70,3 +69,4 @@ public async Task Resource_produces_expected_nullable_properties_in_schema() }); } } + diff --git a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs b/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs deleted file mode 100644 index 03f6531631..0000000000 --- a/test/OpenApiTests/SchemaProperties/SchemaPropertiesStartup.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; -using JetBrains.Annotations; -using JsonApiDotNetCore.Configuration; -using TestBuildingBlocks; - -namespace OpenApiTests.SchemaProperties; - -[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class SchemaPropertiesStartup : OpenApiStartup - where TDbContext : TestableDbContext -{ - protected override void SetJsonApiOptions(JsonApiOptions options) - { - base.SetJsonApiOptions(options); - - options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); - } -} From 7266bc6f1f472a7df0fe48b1db4b53e8e17061ac Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 19 Dec 2022 10:16:54 +0100 Subject: [PATCH 10/66] remove redundant new lines in eof added by cleanupcode --- test/OpenApiTests/OpenApiStartup.cs | 1 - .../SchemaProperties/ModelStateValidationDisabledStartup.cs | 1 - .../ModelStateValidationDisabledTests.cs | 1 - .../ModelStateValidationEnabledTests.cs | 1 - .../NullableReferenceTypesDisabled/NullabilityTests.cs | 1 - .../ModelStateValidationDisabledTests.cs | 1 - .../ModelStateValidationEnabledTests.cs | 1 - .../NullableReferenceTypesEnabled/NullabilityTests.cs | 1 - 8 files changed, 8 deletions(-) diff --git a/test/OpenApiTests/OpenApiStartup.cs b/test/OpenApiTests/OpenApiStartup.cs index a1a46e4022..e34ed1894c 100644 --- a/test/OpenApiTests/OpenApiStartup.cs +++ b/test/OpenApiTests/OpenApiStartup.cs @@ -26,4 +26,3 @@ public override void Configure(IApplicationBuilder app) app.UseEndpoints(endpoints => endpoints.MapControllers()); } } - diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs index ba9ea319d0..1b4dfe7c62 100644 --- a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs @@ -15,4 +15,3 @@ protected override void SetJsonApiOptions(JsonApiOptions options) options.ValidateModelState = false; } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs index 209423df20..a48b3241b5 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -37,4 +37,3 @@ public async Task Produces_expected_required_property_in_schema_for_resource() }); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs index b028eb9cfa..bbf087eb81 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -36,4 +36,3 @@ public async Task Produces_expected_required_property_in_schema_for_resource() }); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index 4e8b216dbe..907cae4400 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -59,4 +59,3 @@ public async Task Produces_expected_nullable_properties_in_schema_for_resource() }); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs index 06083f643c..48fed662f6 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -38,4 +38,3 @@ public async Task Produces_expected_required_property_in_schema_for_resource() }); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs index 034d49be49..7a1092ad33 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -38,4 +38,3 @@ public async Task Produces_expected_required_property_in_schema_for_resource() }); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index d9a7f4b6eb..007376e462 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -69,4 +69,3 @@ public async Task Produces_expected_nullable_properties_in_schema_for_resource() }); } } - From 945a8035ed88a6e5a991a0c64a64bdaddd1d89f9 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 19 Dec 2022 19:59:16 +0100 Subject: [PATCH 11/66] Improved naming in OpenApiTests/SchemaProperties --- .../ModelStateValidationDisabledTests.cs | 6 ++-- .../ModelStateValidationEnabledTests.cs | 6 ++-- .../NullabilityTests.cs | 26 +++++++------- .../ModelStateValidationDisabledTests.cs | 6 ++-- .../ModelStateValidationEnabledTests.cs | 4 +-- .../NullabilityTests.cs | 34 +++++++++---------- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs index a48b3241b5..3a53522397 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -20,15 +20,15 @@ public ModelStateValidationDisabledTests( } [Fact] - public async Task Produces_expected_required_property_in_schema_for_resource() + public async Task Produces_expected_required_property_set_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); requiredAttributes.ShouldHaveCount(3); requiredAttributes.Should().Contain("nameOfCurrentFarm"); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs index bbf087eb81..1eb3562084 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -19,15 +19,15 @@ public ModelStateValidationEnabledTests( } [Fact] - public async Task Produces_expected_required_property_in_schema_for_resource() + public async Task Produces_expected_required_property_set_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(requiredElement => + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); requiredAttributes.ShouldHaveCount(3); requiredAttributes.Should().Contain("nameOfCurrentFarm"); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index 907cae4400..f2ffecc549 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -25,36 +25,36 @@ public async Task Produces_expected_nullable_properties_in_schema_for_resource() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(propertiesElement => + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => { - propertiesElement.ShouldContainPath("name").With(propertyElement => + schemaProperties.ShouldContainPath("name").With(schemaProperty => { - propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); - propertiesElement.ShouldContainPath("nameOfCurrentFarm").With(propertyElement => + schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("age").With(propertyElement => + schemaProperties.ShouldContainPath("age").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("weight").With(propertyElement => + schemaProperties.ShouldContainPath("weight").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => + schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => { - propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); - propertiesElement.ShouldContainPath("hasProducedEggs").With(propertyElement => + schemaProperties.ShouldContainPath("hasProducedEggs").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); }); } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs index 48fed662f6..2f46585b65 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -20,15 +20,15 @@ public ModelStateValidationDisabledTests( } [Fact] - public async Task Produces_expected_required_property_in_schema_for_resource() + public async Task Produces_expected_required_property_set_in_schema_for_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); requiredAttributes.ShouldHaveCount(4); requiredAttributes.Should().Contain("nameOfCurrentFarm"); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs index 7a1092ad33..fd5d56ef97 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -25,9 +25,9 @@ public async Task Produces_expected_required_property_in_schema_for_resource() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(requiredElement => + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(requiredElement.GetRawText()); + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); requiredAttributes.ShouldHaveCount(5); requiredAttributes.Should().Contain("name"); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index 007376e462..4cd5d62ecf 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -25,46 +25,46 @@ public async Task Produces_expected_nullable_properties_in_schema_for_resource() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(propertiesElement => + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => { - propertiesElement.ShouldContainPath("name").With(propertyElement => + schemaProperties.ShouldContainPath("name").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("nameOfCurrentFarm").With(propertyElement => + schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("nameOfPreviousFarm").With(propertyElement => + schemaProperties.ShouldContainPath("nameOfPreviousFarm").With(schemaProperty => { - propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); - propertiesElement.ShouldContainPath("nickname").With(propertyElement => + schemaProperties.ShouldContainPath("nickname").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("age").With(propertyElement => + schemaProperties.ShouldContainPath("age").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("weight").With(propertyElement => + schemaProperties.ShouldContainPath("weight").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); - propertiesElement.ShouldContainPath("timeAtCurrentFarmInDays").With(propertyElement => + schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => { - propertyElement.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); }); - propertiesElement.ShouldContainPath("hasProducedMilk").With(propertyElement => + schemaProperties.ShouldContainPath("hasProducedMilk").With(schemaProperty => { - propertyElement.ShouldNotContainPath("nullable"); + schemaProperty.ShouldNotContainPath("nullable"); }); }); } From f5bb7fc1dbf80b4d3e275f28ac26a682134730f9 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 13:24:12 +0100 Subject: [PATCH 12/66] Review feedback: NullabilityTests --- .../PropertyInfoAssertionsExtension.cs | 16 +----- .../NullabilityTests.cs | 26 ++++----- .../NullabilityTests.cs | 28 +++++---- .../ModelStateValidationDisabledTests.cs | 42 +++++++++++--- .../ModelStateValidationEnabledTests.cs | 42 +++++++++++--- .../NullabilityTests.cs | 43 +++++++------- .../ModelStateValidationDisabledTests.cs | 44 +++++++++++--- .../ModelStateValidationEnabledTests.cs | 47 +++++++++++---- .../NullabilityTests.cs | 57 ++++++++----------- 9 files changed, 213 insertions(+), 132 deletions(-) diff --git a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs index b29c635f8e..e49bef1a0d 100644 --- a/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs +++ b/test/OpenApiClientTests/PropertyInfoAssertionsExtension.cs @@ -7,24 +7,14 @@ namespace OpenApiClientTests; internal static class PropertyInfoAssertionsExtensions { [CustomAssertion] - public static void BeNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) + public static void HaveNullabilityState(this PropertyInfoAssertions source, NullabilityState expected, string because = "", params object[] becauseArgs) { PropertyInfo propertyInfo = source.Subject; NullabilityInfoContext nullabilityContext = new(); NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); - nullabilityInfo.ReadState.Should().NotBe(NullabilityState.NotNull, because, becauseArgs); - } - - [CustomAssertion] - public static void BeNonNullable(this PropertyInfoAssertions source, string because = "", params object[] becauseArgs) - { - PropertyInfo propertyInfo = source.Subject; - - NullabilityInfoContext nullabilityContext = new(); - NullabilityInfo nullabilityInfo = nullabilityContext.Create(propertyInfo); - - nullabilityInfo.ReadState.Should().Be(NullabilityState.NotNull, because, becauseArgs); + nullabilityInfo.ReadState.Should().Be(expected, because, becauseArgs); + nullabilityInfo.WriteState.Should().Be(expected, because, becauseArgs); } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index 136c729537..76c80cbf73 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -7,21 +7,17 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class NullabilityTests { - [Fact] - public void Nullability_of_generated_types_is_as_expected() + [Theory] + [InlineData(nameof(ChickenAttributesInResponse.Name), NullabilityState.Unknown)] + [InlineData(nameof(ChickenAttributesInResponse.NameOfCurrentFarm), NullabilityState.Unknown)] + [InlineData(nameof(ChickenAttributesInResponse.Age), NullabilityState.NotNull)] + [InlineData(nameof(ChickenAttributesInResponse.Weight), NullabilityState.NotNull)] + [InlineData(nameof(ChickenAttributesInResponse.TimeAtCurrentFarmInDays), NullabilityState.Nullable)] + [InlineData(nameof(ChickenAttributesInResponse.HasProducedEggs), NullabilityState.NotNull)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] propertyInfos = typeof(ChickenAttributesInResponse).GetProperties(); - - PropertyInfo? propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.Name)); - propertyInfo.Should().BeNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.NameOfCurrentFarm)); - propertyInfo.Should().BeNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.Age)); - propertyInfo.Should().BeNonNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(ChickenAttributesInResponse.TimeAtCurrentFarmInDays)); - propertyInfo.Should().BeNullable(); + PropertyInfo[] properties = typeof(ChickenAttributesInResponse).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index dfb1f0a934..d351867b8b 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -7,21 +7,19 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; public sealed class NullabilityTests { - [Fact] - public void Nullability_of_generated_types_is_as_expected() + [Theory] + [InlineData(nameof(CowAttributesInResponse.Name), NullabilityState.NotNull)] + [InlineData(nameof(CowAttributesInResponse.NameOfCurrentFarm), NullabilityState.NotNull)] + [InlineData(nameof(CowAttributesInResponse.NameOfPreviousFarm), NullabilityState.Nullable)] + [InlineData(nameof(CowAttributesInResponse.Nickname), NullabilityState.NotNull)] + [InlineData(nameof(CowAttributesInResponse.Age), NullabilityState.NotNull)] + [InlineData(nameof(CowAttributesInResponse.Weight), NullabilityState.NotNull)] + [InlineData(nameof(CowAttributesInResponse.TimeAtCurrentFarmInDays), NullabilityState.Nullable)] + [InlineData(nameof(CowAttributesInResponse.HasProducedMilk), NullabilityState.NotNull)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] propertyInfos = typeof(CowAttributesInResponse).GetProperties(); - - PropertyInfo? propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.Name)); - propertyInfo.Should().BeNonNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.NameOfPreviousFarm)); - propertyInfo.Should().BeNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.Age)); - propertyInfo.Should().BeNonNullable(); - - propertyInfo = propertyInfos.FirstOrDefault(property => property.Name == nameof(CowAttributesInResponse.TimeAtCurrentFarmInDays)); - propertyInfo.Should().BeNullable(); + PropertyInfo[] properties = typeof(CowAttributesInResponse).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs index 3a53522397..0149a07e32 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -19,8 +19,29 @@ public ModelStateValidationDisabledTests( testContext.UseController(); } - [Fact] - public async Task Produces_expected_required_property_set_in_schema_for_resource() + [Theory] + [InlineData("nameOfCurrentFarm")] + [InlineData("weight")] + [InlineData("hasProducedEggs")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("name")] + [InlineData("age")] + [InlineData("timeAtCurrentFarmInDays")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -28,12 +49,19 @@ public async Task Produces_expected_required_property_set_in_schema_for_resource // Assert document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.ShouldHaveCount(3); + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.Should().Contain("nameOfCurrentFarm"); - requiredAttributes.Should().Contain("weight"); - requiredAttributes.Should().Contain("hasProducedEggs"); + requiredProperties.Should().NotContain(propertyName); }); } + + [Fact] + public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs index 1eb3562084..e200fe365c 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -18,8 +18,29 @@ public ModelStateValidationEnabledTests( testContext.UseController(); } - [Fact] - public async Task Produces_expected_required_property_set_in_schema_for_resource() + [Theory] + [InlineData("nameOfCurrentFarm")] + [InlineData("weight")] + [InlineData("hasProducedEggs")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string attributeName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain(attributeName); + }); + } + + [Theory] + [InlineData("name")] + [InlineData("age")] + [InlineData("timeAtCurrentFarmInDays")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string attributeName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -27,12 +48,19 @@ public async Task Produces_expected_required_property_set_in_schema_for_resource // Assert document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.ShouldHaveCount(3); + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.Should().Contain("nameOfCurrentFarm"); - requiredAttributes.Should().Contain("weight"); - requiredAttributes.Should().Contain("hasProducedEggs"); + requiredProperties.Should().NotContain(attributeName); }); } + + [Fact] + public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index f2ffecc549..a5485de6e5 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -18,8 +18,10 @@ public NullabilityTests(OpenApiTestContext { - schemaProperties.ShouldContainPath("name").With(schemaProperty => + schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); + }); + } - schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("age").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("weight").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); + [Theory] + [InlineData("nameOfCurrentFarm")] + [InlineData("age")] + [InlineData("weight")] + [InlineData("hasProducedEggs")] + public async Task Property_in_schema_for_resource_should_not_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - schemaProperties.ShouldContainPath("hasProducedEggs").With(schemaProperty => + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs index 2f46585b65..988fe06896 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -19,8 +19,30 @@ public ModelStateValidationDisabledTests( testContext.UseController(); } - [Fact] - public async Task Produces_expected_required_property_set_in_schema_for_resource() + [Theory] + [InlineData("nameOfCurrentFarm")] + [InlineData("nickname")] + [InlineData("weight")] + [InlineData("hasProducedMilk")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("name")] + [InlineData("age")] + [InlineData("timeAtCurrentFarmInDays")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -28,13 +50,19 @@ public async Task Produces_expected_required_property_set_in_schema_for_resource // Assert document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.ShouldHaveCount(4); + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.Should().Contain("nameOfCurrentFarm"); - requiredAttributes.Should().Contain("nickname"); - requiredAttributes.Should().Contain("weight"); - requiredAttributes.Should().Contain("hasProducedMilk"); + requiredProperties.Should().NotContain(propertyName); }); } + + [Fact] + public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs index fd5d56ef97..70e4b3f7a4 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -18,8 +18,13 @@ public ModelStateValidationEnabledTests( testContext.UseController(); } - [Fact] - public async Task Produces_expected_required_property_in_schema_for_resource() + [Theory] + [InlineData("name")] + [InlineData("nameOfCurrentFarm")] + [InlineData("nickname")] + [InlineData("weight")] + [InlineData("hasProducedMilk")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -27,14 +32,36 @@ public async Task Produces_expected_required_property_in_schema_for_resource() // Assert document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - requiredAttributes.ShouldHaveCount(5); - - requiredAttributes.Should().Contain("name"); - requiredAttributes.Should().Contain("nameOfCurrentFarm"); - requiredAttributes.Should().Contain("nickname"); - requiredAttributes.Should().Contain("weight"); - requiredAttributes.Should().Contain("hasProducedMilk"); + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain(propertyName); }); } + + [Theory] + [InlineData("age")] + [InlineData("timeAtCurrentFarmInDays")] + public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index 4cd5d62ecf..3b8d0597d7 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -18,8 +18,10 @@ public NullabilityTests(OpenApiTestContext { - schemaProperties.ShouldContainPath("name").With(schemaProperty => + schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("nameOfPreviousFarm").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); - }); - - schemaProperties.ShouldContainPath("nickname").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("age").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - - schemaProperties.ShouldContainPath("weight").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); + }); + } - schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(element => element.ValueKind.Should().Be(JsonValueKind.True)); - }); + [Theory] + [InlineData("name")] + [InlineData("nameOfCurrentFarm")] + [InlineData("nickname")] + [InlineData("age")] + [InlineData("weight")] + [InlineData("hasProducedMilk")] + public async Task Property_in_schema_for_resource_should_not_be_nullable(string attributeName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - schemaProperties.ShouldContainPath("hasProducedMilk").With(schemaProperty => + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath(attributeName).With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); From 919a3f8161424b75bcaaabc2e94aedefe804f7f9 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 13:41:10 +0100 Subject: [PATCH 13/66] Improved JsonApiClient and testing SchemaProperty Tests: * More rigorous test suite, see PR description IJsonApiClient: * Renamed registration method to a more functionally descriptive name. * Improved documentation to contain most relevant information on top instead of at the end, and removed ambiguigity in wording. JsonApiClient * Fix bug: disallow omitting members that are explicitly required by the OAS description * Renamed AttributeNamesContainer to AttributesObjectContext because it was more than just a container * Misc: better variable naming --- .../IJsonApiClient.cs | 19 +- .../JsonApiClient.cs | 234 ++++++++++++---- .../UnreachableCodeException.cs | 9 + ...questDocumentRegistrationLifetimeTests.cs} | 44 +-- .../LegacyClient/RequestTests.cs | 4 +- test/OpenApiClientTests/ObjectExtensions.cs | 20 ++ .../RequiredAttributesTests.cs | 249 +++++++++++++++-- .../RequiredAttributesTests.cs | 251 ++++++++++++++++-- 8 files changed, 711 insertions(+), 119 deletions(-) create mode 100644 src/JsonApiDotNetCore.OpenApi.Client/UnreachableCodeException.cs rename test/OpenApiClientTests/LegacyClient/{ClientAttributeRegistrationLifeTimeTests.cs => RequestDocumentRegistrationLifetimeTests.cs} (79%) create mode 100644 test/OpenApiClientTests/ObjectExtensions.cs diff --git a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs index a994d78a0e..18c90fdfd2 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs @@ -5,14 +5,19 @@ namespace JsonApiDotNetCore.OpenApi.Client; public interface IJsonApiClient { /// - /// Ensures correct serialization of attributes in a POST/PATCH Resource request body. In JSON:API, an omitted attribute indicates to ignore it, while an - /// attribute that is set to "null" means to clear it. This poses a problem because the serializer cannot distinguish between "you have explicitly set - /// this .NET property to null" vs "you didn't touch it, so it is null by default" when converting an instance to JSON. Therefore, calling this method - /// treats all attributes that contain their default value (null for reference types, 0 for integers, false for booleans, etc) as - /// omitted unless explicitly listed to include them using . + /// + /// Calling this method ensures that attributes containing a default value (null for reference types, 0 for integers, false for + /// booleans, etc) are omitted during serialization, except for those explicitly marked for inclusion in + /// . + /// + /// + /// This is sometimes required to ensure correct serialization of attributes during a POST/PATCH request. In JSON:API, an omitted attribute indicates to + /// ignore it, while an attribute that is set to "null" means to clear it. This poses a problem because the serializer cannot distinguish between "you + /// have explicitly set this .NET property to null" vs "you didn't touch it, so it is null by default" when converting an instance to JSON. + /// /// /// - /// The request document instance for which this registration applies. + /// The request document instance for which default values should be omitted. /// /// /// Optional. A list of expressions to indicate which properties to unconditionally include in the JSON request body. For example: @@ -30,7 +35,7 @@ public interface IJsonApiClient /// An to clear the current registration. For efficient memory usage, it is recommended to wrap calls to this method in a /// using statement, so the registrations are cleaned up after executing the request. /// - IDisposable RegisterAttributesForRequestDocument(TRequestDocument requestDocument, + IDisposable OmitDefaultValuesForAttributesInRequestDocument(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) where TRequestDocument : class; } diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 219ac04353..03ea71e8e0 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace JsonApiDotNetCore.OpenApi.Client; @@ -23,7 +24,7 @@ protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings) } /// - public IDisposable RegisterAttributesForRequestDocument(TRequestDocument requestDocument, + public IDisposable OmitDefaultValuesForAttributesInRequestDocument(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) where TRequestDocument : class { @@ -43,9 +44,10 @@ public IDisposable RegisterAttributesForRequestDocument _alwaysIncludedAttributesPerRequestDocumentInstance = new(); - private readonly Dictionary> _requestDocumentInstancesPerRequestDocumentType = new(); - private bool _isSerializing; + private readonly Dictionary _attributesObjectInfoByRequestDocument = new(); + private readonly Dictionary> _requestDocumentsByType = new(); + private SerializationScope? _serializationScope; public override bool CanRead => false; - public void RegisterRequestDocument(object requestDocument, AttributeNamesContainer attributes) + public void RegisterRequestDocumentForAttributesOmission(object requestDocument, AttributesObjectInfo attributesObjectInfo) { - _alwaysIncludedAttributesPerRequestDocumentInstance[requestDocument] = attributes; + _attributesObjectInfoByRequestDocument[requestDocument] = attributesObjectInfo; Type requestDocumentType = requestDocument.GetType(); - if (!_requestDocumentInstancesPerRequestDocumentType.ContainsKey(requestDocumentType)) + if (!_requestDocumentsByType.ContainsKey(requestDocumentType)) { - _requestDocumentInstancesPerRequestDocumentType[requestDocumentType] = new HashSet(); + _requestDocumentsByType[requestDocumentType] = new HashSet(); } - _requestDocumentInstancesPerRequestDocumentType[requestDocumentType].Add(requestDocument); + _requestDocumentsByType[requestDocumentType].Add(requestDocument); } - public void RemoveAttributeRegistration(object requestDocument) + public void RemoveRegistration(object requestDocument) { - if (_alwaysIncludedAttributesPerRequestDocumentInstance.ContainsKey(requestDocument)) + if (_attributesObjectInfoByRequestDocument.ContainsKey(requestDocument)) { - _alwaysIncludedAttributesPerRequestDocumentInstance.Remove(requestDocument); + _attributesObjectInfoByRequestDocument.Remove(requestDocument); Type requestDocumentType = requestDocument.GetType(); - _requestDocumentInstancesPerRequestDocumentType[requestDocumentType].Remove(requestDocument); + _requestDocumentsByType[requestDocumentType].Remove(requestDocument); - if (!_requestDocumentInstancesPerRequestDocumentType[requestDocumentType].Any()) + if (!_requestDocumentsByType[requestDocumentType].Any()) { - _requestDocumentInstancesPerRequestDocumentType.Remove(requestDocumentType); + _requestDocumentsByType.Remove(requestDocumentType); } } } @@ -107,71 +109,195 @@ public override bool CanConvert(Type objectType) { ArgumentGuard.NotNull(objectType); - return !_isSerializing && _requestDocumentInstancesPerRequestDocumentType.ContainsKey(objectType); + if (_serializationScope == null) + { + return _requestDocumentsByType.ContainsKey(objectType); + } + + return _serializationScope.ShouldConvertAsAttributesObject(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { - throw new Exception("This code should not be reachable."); + throw new UnreachableCodeException(); } public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { ArgumentGuard.NotNull(writer); + ArgumentGuard.NotNull(value); ArgumentGuard.NotNull(serializer); - if (value != null) + if (_serializationScope == null) { - if (_alwaysIncludedAttributesPerRequestDocumentInstance.ContainsKey(value)) - { - AttributeNamesContainer attributeNamesContainer = _alwaysIncludedAttributesPerRequestDocumentInstance[value]; - serializer.ContractResolver = new JsonApiDocumentContractResolver(attributeNamesContainer); - } + AssertObjectIsRequestDocument(value); - try - { - _isSerializing = true; - serializer.Serialize(writer, value); - } - finally + SerializeRequestDocument(writer, value, serializer); + } + else + { + AttributesObjectInfo? attributesObjectInfo = _serializationScope.AttributesObjectInScope; + + AssertObjectMatchesSerializationScope(attributesObjectInfo, value); + + SerializeAttributesObject(attributesObjectInfo, writer, value, serializer); + } + } + + private void AssertObjectIsRequestDocument(object value) + { + Type objectType = value.GetType(); + + if (!_requestDocumentsByType.ContainsKey(objectType)) + { + throw new UnreachableCodeException(); + } + } + + private void SerializeRequestDocument(JsonWriter writer, object value, JsonSerializer serializer) + { + _serializationScope = new SerializationScope(); + + if (_attributesObjectInfoByRequestDocument.TryGetValue(value, out AttributesObjectInfo? attributesObjectInfo)) + { + _serializationScope.AttributesObjectInScope = attributesObjectInfo; + } + + try + { + serializer.Serialize(writer, value); + } + finally + { + _serializationScope = null; + } + } + + private static void AssertObjectMatchesSerializationScope([SysNotNull] AttributesObjectInfo? attributesObjectInfo, object value) + { + Type objectType = value.GetType(); + + if (attributesObjectInfo == null || !attributesObjectInfo.MatchesType(objectType)) + { + throw new UnreachableCodeException(); + } + } + + private static void SerializeAttributesObject(AttributesObjectInfo alwaysIncludedAttributes, JsonWriter writer, object value, JsonSerializer serializer) + { + AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes); + + serializer.ContractResolver = new JsonApiAttributeContractResolver(alwaysIncludedAttributes); + serializer.Serialize(writer, value); + } + + private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributesObjectInfo alwaysIncludedAttributes) + { + PropertyInfo[] propertyInfos = value.GetType().GetProperties(); + + foreach (PropertyInfo attributesPropertyInfo in propertyInfos) + { + bool isExplicitlyIncluded = alwaysIncludedAttributes.IsAttributeMarkedForInclusion(attributesPropertyInfo.Name); + + if (isExplicitlyIncluded) { - _isSerializing = false; + return; } + + AssertRequiredPropertyIsNotIgnored(value, attributesPropertyInfo); + } + } + + private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInfo attribute) + { + JsonPropertyAttribute jsonPropertyForAttribute = attribute.GetCustomAttributes().Single(); + + if (jsonPropertyForAttribute.Required != Required.Always) + { + return; + } + + bool isPropertyIgnored = DefaultValueEqualsCurrentValue(attribute, value); + + if (isPropertyIgnored) + { + throw new JsonSerializationException( + $"Ignored property '{jsonPropertyForAttribute.PropertyName}' must have a value because it is required. Path 'data.attributes'."); } } + + private static bool DefaultValueEqualsCurrentValue(PropertyInfo propertyInfo, object instance) + { + object? currentValue = propertyInfo.GetValue(instance); + object? defaultValue = GetDefaultValue(propertyInfo.PropertyType); + + if (defaultValue == null) + { + return currentValue == null; + } + + return defaultValue.Equals(currentValue); + } + + private static object? GetDefaultValue(Type type) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + } + + private sealed class SerializationScope + { + private bool _isFirstAttemptToConvertAttributes = true; + public AttributesObjectInfo? AttributesObjectInScope { get; set; } + + public bool ShouldConvertAsAttributesObject(Type type) + { + if (!_isFirstAttemptToConvertAttributes || AttributesObjectInScope == null) + { + return false; + } + + if (!AttributesObjectInScope.MatchesType(type)) + { + return false; + } + + _isFirstAttemptToConvertAttributes = false; + return true; + } } - private sealed class AttributeNamesContainer + private sealed class AttributesObjectInfo { - private readonly ISet _attributeNames; - private readonly Type _containerType; + private readonly ISet _attributesMarkedForInclusion; + private readonly Type _attributesObjectType; - public AttributeNamesContainer(ISet attributeNames, Type containerType) + public AttributesObjectInfo(ISet attributesMarkedForInclusion, Type attributesObjectType) { - ArgumentGuard.NotNull(attributeNames); - ArgumentGuard.NotNull(containerType); + ArgumentGuard.NotNull(attributesMarkedForInclusion); + ArgumentGuard.NotNull(attributesObjectType); - _attributeNames = attributeNames; - _containerType = containerType; + _attributesMarkedForInclusion = attributesMarkedForInclusion; + _attributesObjectType = attributesObjectType; } - public bool ContainsAttribute(string name) + public bool IsAttributeMarkedForInclusion(string name) { - return _attributeNames.Contains(name); + return _attributesMarkedForInclusion.Contains(name); } - public bool ContainerMatchesType(Type type) + public bool MatchesType(Type type) { - return _containerType == type; + return _attributesObjectType == type; } } - private sealed class AttributesRegistrationScope : IDisposable + private sealed class RequestDocumentRegistrationScope : IDisposable { private readonly JsonApiJsonConverter _jsonApiJsonConverter; private readonly object _requestDocument; - public AttributesRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, object requestDocument) + public RequestDocumentRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, object requestDocument) { ArgumentGuard.NotNull(jsonApiJsonConverter); ArgumentGuard.NotNull(requestDocument); @@ -182,28 +308,28 @@ public AttributesRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, ob public void Dispose() { - _jsonApiJsonConverter.RemoveAttributeRegistration(_requestDocument); + _jsonApiJsonConverter.RemoveRegistration(_requestDocument); } } - private sealed class JsonApiDocumentContractResolver : DefaultContractResolver + private sealed class JsonApiAttributeContractResolver : DefaultContractResolver { - private readonly AttributeNamesContainer _attributeNamesContainer; + private readonly AttributesObjectInfo _attributesObjectInfo; - public JsonApiDocumentContractResolver(AttributeNamesContainer attributeNamesContainer) + public JsonApiAttributeContractResolver(AttributesObjectInfo attributesObjectInfo) { - ArgumentGuard.NotNull(attributeNamesContainer); + ArgumentGuard.NotNull(attributesObjectInfo); - _attributeNamesContainer = attributeNamesContainer; + _attributesObjectInfo = attributesObjectInfo; } protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); - if (_attributeNamesContainer.ContainerMatchesType(property.DeclaringType!)) + if (_attributesObjectInfo.MatchesType(property.DeclaringType!)) { - if (_attributeNamesContainer.ContainsAttribute(property.UnderlyingName!)) + if (_attributesObjectInfo.IsAttributeMarkedForInclusion(property.UnderlyingName!)) { property.NullValueHandling = NullValueHandling.Include; property.DefaultValueHandling = DefaultValueHandling.Include; diff --git a/src/JsonApiDotNetCore.OpenApi.Client/UnreachableCodeException.cs b/src/JsonApiDotNetCore.OpenApi.Client/UnreachableCodeException.cs new file mode 100644 index 0000000000..f1821329d0 --- /dev/null +++ b/src/JsonApiDotNetCore.OpenApi.Client/UnreachableCodeException.cs @@ -0,0 +1,9 @@ +namespace JsonApiDotNetCore.OpenApi.Client; + +internal sealed class UnreachableCodeException : Exception +{ + public UnreachableCodeException() + : base("This code should not be reachable.") + { + } +} diff --git a/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs similarity index 79% rename from test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs rename to test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs index 4582f9578d..73e134fa2d 100644 --- a/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs @@ -6,10 +6,10 @@ namespace OpenApiClientTests.LegacyClient; -public sealed class ClientAttributeRegistrationLifetimeTests +public sealed class RequestDocumentRegistrationLifetimeTests { [Fact] - public async Task Disposed_attribute_registration_for_document_does_not_affect_request() + public async Task Disposed_request_document_registration_does_not_affect_request() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -27,7 +27,7 @@ public async Task Disposed_attribute_registration_for_document_does_not_affect_r } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); @@ -51,7 +51,7 @@ public async Task Disposed_attribute_registration_for_document_does_not_affect_r } [Fact] - public async Task Attribute_registration_can_be_used_for_multiple_requests() + public async Task Request_document_registration_can_be_used_for_multiple_requests() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -72,7 +72,7 @@ public async Task Attribute_registration_can_be_used_for_multiple_requests() } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); @@ -98,7 +98,7 @@ public async Task Attribute_registration_can_be_used_for_multiple_requests() } [Fact] - public async Task Request_is_unaffected_by_attribute_registration_for_different_document_of_same_type() + public async Task Request_is_unaffected_by_request_document_registration_of_different_request_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -128,10 +128,10 @@ public async Task Request_is_unaffected_by_attribute_registration_for_different_ } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument1, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, airplane => airplane.AirtimeInHours)) { - using (apiClient.RegisterAttributesForRequestDocument(requestDocument2, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, airplane => airplane.SerialNumber)) { } @@ -153,7 +153,7 @@ public async Task Request_is_unaffected_by_attribute_registration_for_different_ } [Fact] - public async Task Attribute_values_can_be_changed_after_attribute_registration() + public async Task Attribute_values_can_be_changed_after_request_document_registration() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -174,7 +174,7 @@ public async Task Attribute_values_can_be_changed_after_attribute_registration() } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, airplane => airplane.IsInMaintenance)) { requestDocument.Data.Attributes.IsInMaintenance = false; @@ -196,7 +196,7 @@ public async Task Attribute_values_can_be_changed_after_attribute_registration() } [Fact] - public async Task Attribute_registration_is_unaffected_by_successive_attribute_registration_for_document_of_different_type() + public async Task Request_document_registration_is_unaffected_by_successive_registration_of_request_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -223,10 +223,10 @@ public async Task Attribute_registration_is_unaffected_by_successive_attribute_r } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument1, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, airplane => airplane.IsInMaintenance)) { - using (apiClient.RegisterAttributesForRequestDocument(requestDocument2, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, airplane => airplane.AirtimeInHours)) { // Act @@ -247,7 +247,7 @@ public async Task Attribute_registration_is_unaffected_by_successive_attribute_r } [Fact] - public async Task Attribute_registration_is_unaffected_by_preceding_disposed_attribute_registration_for_different_document_of_same_type() + public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_different_request_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -265,7 +265,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument1, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, requestDocument1)); @@ -288,7 +288,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att wrapper.ChangeResponse(HttpStatusCode.NoContent, null); - using (apiClient.RegisterAttributesForRequestDocument(requestDocument2, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, airplane => airplane.SerialNumber)) { // Act @@ -309,7 +309,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att } [Fact] - public async Task Attribute_registration_is_unaffected_by_preceding_disposed_attribute_registration_for_document_of_different_type() + public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_request_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -324,7 +324,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument1, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(requestDocument1)); @@ -347,7 +347,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att wrapper.ChangeResponse(HttpStatusCode.NoContent, null); - using (apiClient.RegisterAttributesForRequestDocument(requestDocument2, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, airplane => airplane.SerialNumber)) { // Act @@ -368,7 +368,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att } [Fact] - public async Task Attribute_registration_is_unaffected_by_preceding_attribute_registration_for_different_document_of_same_type() + public async Task Request_document_registration_is_unaffected_by_preceding_registration_of_different_request_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -398,10 +398,10 @@ public async Task Attribute_registration_is_unaffected_by_preceding_attribute_re } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument1, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, airplane => airplane.SerialNumber)) { - using (apiClient.RegisterAttributesForRequestDocument(requestDocument2, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act diff --git a/test/OpenApiClientTests/LegacyClient/RequestTests.cs b/test/OpenApiClientTests/LegacyClient/RequestTests.cs index e03e8f1015..8bb0c3f350 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestTests.cs @@ -152,7 +152,7 @@ public async Task Partial_posting_resource_produces_expected_request() } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, airplane => airplane.SerialNumber)) { // Act @@ -203,7 +203,7 @@ public async Task Partial_patching_resource_produces_expected_request() } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, airplane => airplane.SerialNumber, airplane => airplane.LastServicedAt, airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs new file mode 100644 index 0000000000..e3790eaa15 --- /dev/null +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using JsonApiDotNetCore.OpenApi.Client; + +namespace OpenApiClientTests; + +internal static class ObjectExtensions +{ + public static void SetPropertyToDefaultValue(this object target, string propertyName) + { + ArgumentGuard.NotNull(target); + ArgumentGuard.NotNull(propertyName); + + Type declaringType = target.GetType(); + + PropertyInfo property = declaringType.GetProperties().Single(property => property.Name == propertyName); + object? defaultValue = declaringType.IsValueType ? Activator.CreateInstance(declaringType) : null; + + property.SetValue(target, defaultValue); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs index b97535f908..f43f74543a 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs @@ -12,10 +12,10 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class RequiredAttributesTests { - private const string HostPrefix = "http://localhost/"; + private const string ChickenUrl = "http://localhost/chickens"; [Fact] - public async Task Partial_posting_resource_with_explicitly_omitting_required_fields_produces_expected_request() + public async Task Can_exclude_optional_attributes() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -27,13 +27,14 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie { Attributes = new ChickenAttributesInPostRequest { + NameOfCurrentFarm = "Cow and Chicken Farm", + Weight = 30, HasProducedEggs = true } } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument, - chicken => chicken.HasProducedEggs)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) { // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); @@ -43,7 +44,7 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HostPrefix + "chickens"); + wrapper.Request.RequestUri.Should().Be(ChickenUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -52,43 +53,109 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie ""data"": { ""type"": ""chickens"", ""attributes"": { + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""weight"": 30, ""hasProducedEggs"": true } } }"); } - [Fact] - public async Task Partial_posting_resource_without_explicitly_omitting_required_fields_fails() + [Theory] + [InlineData(nameof(ChickenAttributesInResponse.NameOfCurrentFarm), "nameOfCurrentFarm")] + [InlineData(nameof(ChickenAttributesInResponse.Weight), "weight")] + [InlineData(nameof(ChickenAttributesInResponse.HasProducedEggs), "hasProducedEggs")] + public async Task Cannot_exclude_required_attribute_when_performing_POST(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + var attributesInPostRequest = new ChickenAttributesInPostRequest + { + Name = "Chicken", + NameOfCurrentFarm = "Cow and Chicken Farm", + Age = 10, + Weight = 30, + TimeAtCurrentFarmInDays = 100, + HasProducedEggs = true + }; + + attributesInPostRequest.SetPropertyToDefaultValue(propertyName); + var requestDocument = new ChickenPostRequestDocument { Data = new ChickenDataInPostRequest { - Attributes = new ChickenAttributesInPostRequest + Attributes = attributesInPostRequest + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_exclude_attributes_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPatchRequestDocument + { + Data = new ChickenDataInPatchRequest + { + Id = "1", + Attributes = new ChickenAttributesInPatchRequest { - Weight = 3 + Name = "Chicken", + Age = 10, + TimeAtCurrentFarmInDays = 100 } } }; // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(1, requestDocument)); + } // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(ChickenUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"", + ""attributes"": { + ""name"": ""Chicken"", + ""age"": 10, + ""timeAtCurrentFarmInDays"": 100 + } + } +}"); } [Fact] - public async Task Patching_resource_with_missing_id_fails() + public async Task Cannot_exclude_id_when_performing_PATCH() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -100,14 +167,164 @@ public async Task Patching_resource_with_missing_id_fails() { Attributes = new ChickenAttributesInPatchRequest { - Age = 1 + Name = "Chicken", + NameOfCurrentFarm = "Cow and Chicken Farm", + Age = 10, + Weight = 30, + TimeAtCurrentFarmInDays = 100, + HasProducedEggs = true } } }; + // Act Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(1, requestDocument)); // Assert await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Fact] + public async Task Can_clear_nullable_attributes() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPostRequestDocument + { + Data = new ChickenDataInPostRequest + { + Attributes = new ChickenAttributesInPostRequest + { + Name = null, + TimeAtCurrentFarmInDays = null, + NameOfCurrentFarm = "Cow and Chicken Farm", + Age = 10, + Weight = 30, + HasProducedEggs = true + } + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + chicken => chicken.Name, chicken => chicken.TimeAtCurrentFarmInDays)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(ChickenUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""chickens"", + ""attributes"": { + ""name"": null, + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""age"": 10, + ""weight"": 30, + ""timeAtCurrentFarmInDays"": null, + ""hasProducedEggs"": true + } + } +}"); + } + + [Fact] + public async Task Cannot_clear_required_attribute_when_performing_POST() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPostRequestDocument + { + Data = new ChickenDataInPostRequest + { + Attributes = new ChickenAttributesInPostRequest + { + Name = "Chicken", + NameOfCurrentFarm = null, + Age = 10, + Weight = 30, + TimeAtCurrentFarmInDays = 100, + HasProducedEggs = true + } + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + chicken => chicken.NameOfCurrentFarm)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); + } + + [Fact] + public async Task Can_set_default_value_to_ValueType_attributes() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new ChickenPostRequestDocument + { + Data = new ChickenDataInPostRequest + { + Attributes = new ChickenAttributesInPostRequest + { + Name = "Chicken", + NameOfCurrentFarm = "Cow and Chicken Farm", + TimeAtCurrentFarmInDays = 100 + } + } + }; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(ChickenUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""chickens"", + ""attributes"": { + ""name"": ""Chicken"", + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""age"": 0, + ""weight"": 0, + ""timeAtCurrentFarmInDays"": 100, + ""hasProducedEggs"": false + } + } +}"); } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs index 5f35ec7f29..aa3e4b512c 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs @@ -12,10 +12,10 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; public sealed class RequiredAttributesTests { - private const string HostPrefix = "http://localhost/"; + private const string CowUrl = "http://localhost/cows"; [Fact] - public async Task Partial_posting_resource_with_explicitly_omitting_required_fields_produces_expected_request() + public async Task Can_exclude_optional_attributes() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -27,13 +27,16 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie { Attributes = new CowAttributesInPostRequest { - HasProducedMilk = true, - Weight = 1100 + Name = "Cow", + NameOfCurrentFarm = "Cow and Chicken Farm", + Nickname = "Cow", + Weight = 30, + HasProducedMilk = true } } }; - using (apiClient.RegisterAttributesForRequestDocument(requestDocument)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) { // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); @@ -43,7 +46,7 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HostPrefix + "cows"); + wrapper.Request.RequestUri.Should().Be(CowUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -52,43 +55,255 @@ public async Task Partial_posting_resource_with_explicitly_omitting_required_fie ""data"": { ""type"": ""cows"", ""attributes"": { - ""weight"": 1100, + ""name"": ""Cow"", + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""nickname"": ""Cow"", + ""weight"": 30, ""hasProducedMilk"": true } } }"); } - [Fact] - public async Task Partial_posting_resource_without_explicitly_omitting_required_fields_produces_expected_request() + [Theory] + [InlineData(nameof(CowAttributesInResponse.Name), "name")] + [InlineData(nameof(CowAttributesInResponse.NameOfCurrentFarm), "nameOfCurrentFarm")] + [InlineData(nameof(CowAttributesInResponse.Nickname), "nickname")] + [InlineData(nameof(CowAttributesInResponse.Weight), "weight")] + [InlineData(nameof(CowAttributesInResponse.HasProducedMilk), "hasProducedMilk")] + public async Task Cannot_exclude_required_attribute_when_performing_POST(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + var attributesInPostRequest = new CowAttributesInPostRequest + { + Name = "Cow", + NameOfCurrentFarm = "Cow and Chicken Farm", + NameOfPreviousFarm = "Animal Farm", + Nickname = "Cow", + Age = 10, + Weight = 30, + TimeAtCurrentFarmInDays = 100, + HasProducedMilk = true + }; + + attributesInPostRequest.SetPropertyToDefaultValue(propertyName); + var requestDocument = new CowPostRequestDocument { Data = new CowDataInPostRequest { - Attributes = new CowAttributesInPostRequest + Attributes = attributesInPostRequest + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_exclude_attributes_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPatchRequestDocument + { + Data = new CowDataInPatchRequest + { + Id = "1", + Attributes = new CowAttributesInPatchRequest + { + NameOfPreviousFarm = "Animal Farm", + Age = 10, + TimeAtCurrentFarmInDays = 100 + } + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(1, requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(CowUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cows"", + ""id"": ""1"", + ""attributes"": { + ""nameOfPreviousFarm"": ""Animal Farm"", + ""age"": 10, + ""timeAtCurrentFarmInDays"": 100 + } + } +}"); + } + + [Fact] + public async Task Cannot_exclude_id_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPatchRequestDocument + { + Data = new CowDataInPatchRequest + { + Attributes = new CowAttributesInPatchRequest { - Weight = 1100, - Age = 5, - Name = "Cow 1", - NameOfCurrentFarm = "123", - NameOfPreviousFarm = "123" + Name = "Cow", + NameOfCurrentFarm = "Cow and Chicken Farm", + NameOfPreviousFarm = "Animal Farm", + Nickname = "Cow", + Age = 10, + Weight = 30, + TimeAtCurrentFarmInDays = 100, + HasProducedMilk = true } } }; // Act - Func> - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(1, requestDocument)); // Assert + await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be("Cannot write a null value for property 'nickname'. Property requires a value. Path 'data.attributes'."); + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Fact] + public async Task Can_clear_nullable_attributes() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPostRequestDocument + { + Data = new CowDataInPostRequest + { + Attributes = new CowAttributesInPostRequest + { + NameOfPreviousFarm = null, + TimeAtCurrentFarmInDays = null, + Name = "Cow", + NameOfCurrentFarm = "Cow and Chicken Farm", + Nickname = "Cow", + Age = 10, + Weight = 30, + HasProducedMilk = true + } + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + cow => cow.NameOfPreviousFarm, cow => cow.TimeAtCurrentFarmInDays)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(CowUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cows"", + ""attributes"": { + ""name"": ""Cow"", + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""nameOfPreviousFarm"": null, + ""nickname"": ""Cow"", + ""age"": 10, + ""weight"": 30, + ""timeAtCurrentFarmInDays"": null, + ""hasProducedMilk"": true + } + } +}"); + } + + [Fact] + public async Task Can_set_default_value_to_ValueType_attributes() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowPostRequestDocument + { + Data = new CowDataInPostRequest + { + Attributes = new CowAttributesInPostRequest + { + Name = "Cow", + NameOfCurrentFarm = "Cow and Chicken Farm", + NameOfPreviousFarm = "Animal Farm", + Nickname = "Cow", + TimeAtCurrentFarmInDays = 100 + } + } + }; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(CowUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cows"", + ""attributes"": { + ""name"": ""Cow"", + ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", + ""nameOfPreviousFarm"": ""Animal Farm"", + ""nickname"": ""Cow"", + ""age"": 0, + ""weight"": 0, + ""timeAtCurrentFarmInDays"": 100, + ""hasProducedMilk"": false + } + } +}"); } } From 87aea3e668d96059aaf482f3ee3d0a3d8c101d06 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 13:56:51 +0100 Subject: [PATCH 14/66] Fix test: should not omit required field in test request body --- .../LegacyClient/RequestDocumentRegistrationLifetimeTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs index 73e134fa2d..55e882fedf 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs @@ -320,7 +320,10 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo Data = new AirplaneDataInPostRequest { Type = AirplaneResourceType.Airplanes, - Attributes = new AirplaneAttributesInPostRequest() + Attributes = new AirplaneAttributesInPostRequest + { + Name = "Jay Jay the Jet Plane" + } } }; From 2ff06fce5dd49125506fad6036944f9bbf6f3bb1 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 14:06:56 +0100 Subject: [PATCH 15/66] Temp enable CI buid for current branch --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 433d391b16..bbd35ed160 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,6 +14,7 @@ environment: branches: only: + - openapi-required-and-nullable-properties # TODO: remove - master - openapi - develop From 932367e4f98a655abf0af22133e78d994f996cf2 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 16:23:35 +0100 Subject: [PATCH 16/66] Rename test files: it no longer only concerns required attributes, but more generally request behaviour --- .../{RequiredAttributesTests.cs => RequestTests.cs} | 2 +- .../{RequiredAttributesTests.cs => RequestTests.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/{RequiredAttributesTests.cs => RequestTests.cs} (99%) rename test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/{RequiredAttributesTests.cs => RequestTests.cs} (99%) diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs similarity index 99% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs rename to test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs index f43f74543a..b624a756ef 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequiredAttributesTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; -public sealed class RequiredAttributesTests +public sealed class RequestTests { private const string ChickenUrl = "http://localhost/chickens"; diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs similarity index 99% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs rename to test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs index aa3e4b512c..50fdf9c355 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequiredAttributesTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; -public sealed class RequiredAttributesTests +public sealed class RequestTests { private const string CowUrl = "http://localhost/cows"; From 061f4266c610ccb4fa52f3ca8f18862694dab5c3 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 22 Dec 2022 18:14:10 +0100 Subject: [PATCH 17/66] Changes and tests for support of nullable and required for relationships --- .../JsonApiClient.cs | 11 +- .../ResourceFieldObjectSchemaBuilder.cs | 17 +- .../OpenApiClientTests.csproj | 14 + .../NullableReferenceTypesDisabledClient.cs | 14 + .../RelationshipsObject/RequestTests.cs | 558 +++++ .../RelationshipsObject/RequestTestsAlt.cs | 648 ++++++ .../RelationshipsObject/swagger.g.json | 1632 ++++++++++++++ .../NullableReferenceTypesEnabledClient.cs | 14 + .../RelationshipsObject/RequestTests.cs | 487 ++++ .../RelationshipsObject/swagger.g.json | 1954 +++++++++++++++++ test/OpenApiTests/JsonElementExtensions.cs | 19 +- .../ModelStateValidationDisabledTests.cs | 66 + .../ModelStateValidationEnabledTests.cs | 65 + .../RelationshipsObject/NrtDisabledModel.cs | 18 + .../RelationshipsObject/NullabilityTests.cs | 56 + ...NullableReferenceTypesDisabledDbContext.cs | 33 + .../RelationshipsObject/RelationshipModel.cs | 10 + .../NullabilityTests.cs | 5 +- .../ModelStateValidationDisabledTests.cs | 68 + .../ModelStateValidationEnabledTests.cs | 67 + .../RelationshipsObject/NrtEnabledModel.cs | 32 + .../RelationshipsObject/NullabilityTests.cs | 58 + .../NullableReferenceTypesEnabledDbContext.cs | 43 + .../RelationshipsObject/RelationshipModel.cs | 9 + 24 files changed, 5885 insertions(+), 13 deletions(-) create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 03ea71e8e0..79a25e7deb 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -185,13 +185,13 @@ private static void AssertObjectMatchesSerializationScope([SysNotNull] Attribute private static void SerializeAttributesObject(AttributesObjectInfo alwaysIncludedAttributes, JsonWriter writer, object value, JsonSerializer serializer) { - AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes); + AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes, writer); serializer.ContractResolver = new JsonApiAttributeContractResolver(alwaysIncludedAttributes); serializer.Serialize(writer, value); } - private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributesObjectInfo alwaysIncludedAttributes) + private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributesObjectInfo alwaysIncludedAttributes, JsonWriter jsonWriter) { PropertyInfo[] propertyInfos = value.GetType().GetProperties(); @@ -204,11 +204,11 @@ private static void AssertRequiredPropertiesAreNotExcluded(object value, Attribu return; } - AssertRequiredPropertyIsNotIgnored(value, attributesPropertyInfo); + AssertRequiredPropertyIsNotIgnored(value, attributesPropertyInfo, jsonWriter.Path); } } - private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInfo attribute) + private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInfo attribute, string path) { JsonPropertyAttribute jsonPropertyForAttribute = attribute.GetCustomAttributes().Single(); @@ -222,7 +222,7 @@ private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInf if (isPropertyIgnored) { throw new JsonSerializationException( - $"Ignored property '{jsonPropertyForAttribute.PropertyName}' must have a value because it is required. Path 'data.attributes'."); + $"Ignored property '{jsonPropertyForAttribute.PropertyName}' must have a value because it is required. Path '{path}'."); } } @@ -345,3 +345,4 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ } } } + diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index e4abf42549..14cdfcc953 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -112,20 +112,28 @@ private void ExposeSchema(OpenApiReference openApiReference, Type typeRepresente private bool IsFieldRequired(ResourceFieldAttribute field) { - if (field is HasManyAttribute || _resourceTypeInfo.ResourceObjectOpenType != typeof(ResourceObjectInPostRequest<>)) + if (_resourceTypeInfo.ResourceObjectOpenType != typeof(ResourceObjectInPostRequest<>)) { return false; } - bool hasRequiredAttribute = field.Property.HasAttribute(); + if (field.Property.HasAttribute()) + { + return true; + } + + if (field is HasManyAttribute) + { + return false; + } NullabilityInfoContext nullabilityContext = new(); NullabilityInfo nullabilityInfo = nullabilityContext.Create(field.Property); return field.Property.PropertyType.IsValueType switch { - true => hasRequiredAttribute, - false => _options.ValidateModelState ? nullabilityInfo.ReadState == NullabilityState.NotNull || hasRequiredAttribute : hasRequiredAttribute + true => false, + false => _options.ValidateModelState && nullabilityInfo.ReadState == NullabilityState.NotNull }; } @@ -230,3 +238,4 @@ private static bool IsDataPropertyNullableInRelationshipSchemaType(Type relation return NullableRelationshipSchemaOpenTypes.Contains(relationshipSchemaOpenType); } } + diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index badeeaeae7..143668412b 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -66,5 +66,19 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false + + OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode + NullableReferenceTypesEnabledClientRelationshipsObject + NullableReferenceTypesEnabledClientRelationshipsObject.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + + + OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode + NullableReferenceTypesDisabledClientRelationshipsObject + NullableReferenceTypesDisabledClientRelationshipsObject.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false + \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs new file mode 100644 index 0000000000..40ab429277 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; + +internal partial class NullableReferenceTypesDisabledClientRelationshipsObject : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs new file mode 100644 index 0000000000..1dea915b88 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs @@ -0,0 +1,558 @@ +using System.Net; +using System.Reflection; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +public sealed class RequestTests +{ + private const string NrtDisabledModelUrl = "http://localhost/nrtDisabledModels"; + + [Fact] + public async Task Can_exclude_optional_relationships() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtDisabledModelPostRequestDocument() + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = new NrtDisabledModelRelationshipsInPostRequest + { + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""relationships"": { + ""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtDisabledModelPatchRequestDocument() + { + Data = new NrtDisabledModelDataInPatchRequest + { + Id = "1", + Type = NrtDisabledModelResourceType.NrtDisabledModels, + Relationships = new NrtDisabledModelRelationshipsInPatchRequest() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtDisabledModelAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""id"": ""1"", + ""relationships"": { + ""hasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Fact] + public async Task Can_clear_nullable_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtDisabledModelPostRequestDocument() + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = new NrtDisabledModelRelationshipsInPostRequest + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = null + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""relationships"": { + ""hasOne"": { + ""data"": null + }, + ""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + }, + ""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, model => model.RequiredHasOne, model => model.HasMany, model => model.RequiredHasMany)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs new file mode 100644 index 0000000000..8dd29c3fb7 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs @@ -0,0 +1,648 @@ +using System.Net; +using System.Reflection; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +public sealed class RequestTestsAlt +{ + private const string NrtDisabledModelUrl = "http://localhost/nrtDisabledModels"; + + private readonly Dictionary _partials = new() + { + { + nameof(NrtDisabledModelRelationshipsInPostRequest.HasOne), @"""hasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }" + }, + { + nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), @"""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }" + }, + + { + nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), @"""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + }" + }, + + { + nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), @"""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + }" + } + }; + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasOne))] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany))] + public async Task Can_exclude_optional_relationships(string propertyName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsObject = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsObject.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsObject + } + }; + + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + string body = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""relationships"": "+ body +@" + } +}"); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne))] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany))] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH(string propertyName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + var relationshipsObject = new NrtDisabledModelRelationshipsInPatchRequest() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsObject.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtDisabledModelPatchRequestDocument() + { + Data = new NrtDisabledModelDataInPatchRequest + { + Id = "1", + Type = NrtDisabledModelResourceType.NrtDisabledModels, + Relationships = relationshipsObject + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtDisabledModelAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + string serializedRelationshipsObject = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""id"": ""1"", + ""relationships"": "+ serializedRelationshipsObject +@" + } +}"); + } + + [Fact] + public async Task Can_clear_nullable_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtDisabledModelPostRequestDocument() + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = new NrtDisabledModelRelationshipsInPostRequest + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = null + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtDisabledModels"", + ""relationships"": { + ""hasOne"": { + ""data"": null + }, + ""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + }, + ""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, model => model.RequiredHasOne, model => model.HasMany, model => model.RequiredHasMany)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } + } + + [Theory] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] + [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + + NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new NrtDisabledModelPostRequestDocument + { + Data = new NrtDisabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } + + private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludeProperty) + { + string partial = ""; + + foreach ((string key, string relationshipJsonPartial) in _partials) + { + if (excludeProperty == key) + { + continue; + } + + if (partial.Length > 0) + { + partial += ",\n "; + } + + partial += relationshipJsonPartial; + } + + return @"{ + " + partial + @" + }"; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json new file mode 100644 index 0000000000..2d92b99038 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json @@ -0,0 +1,1632 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/nrtDisabledModels": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "postNrtDisabledModel", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/nrtDisabledModels/{id}": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "patchNrtDisabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "deleteNrtDisabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtDisabledModels/{id}/hasMany": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + } + }, + "/nrtDisabledModels/{id}/relationships/hasMany": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "postNrtDisabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "patchNrtDisabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "deleteNrtDisabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtDisabledModels/{id}/hasOne": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtDisabledModels/{id}/relationships/hasOne": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "patchNrtDisabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtDisabledModels/{id}/requiredHasMany": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelRequiredHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelRequiredHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + } + }, + "/nrtDisabledModels/{id}/relationships/requiredHasMany": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "postNrtDisabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "patchNrtDisabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "deleteNrtDisabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtDisabledModels/{id}/requiredHasOne": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtDisabledModels/{id}/relationships/requiredHasOne": { + "get": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "getNrtDisabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "headNrtDisabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtDisabledModels" + ], + "operationId": "patchNrtDisabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "components": { + "schemas": { + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nrtDisabledModelDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtDisabledModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "relationships": { + "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtDisabledModelResourceType" + }, + "relationships": { + "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtDisabledModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "relationships": { + "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nrtDisabledModelPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtDisabledModelDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtDisabledModelDataInPostRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtDisabledModelDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelRelationshipsInPostRequest": { + "required": [ + "requiredHasMany", + "requiredHasOne" + ], + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelRelationshipsInResponse": { + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInResponse" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInResponse" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInResponse" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInResponse" + } + }, + "additionalProperties": false + }, + "nrtDisabledModelResourceType": { + "enum": [ + "nrtDisabledModels" + ], + "type": "string" + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullableRelationshipModelIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableRelationshipModelSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "relationshipModelCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/relationshipModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/relationshipModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelResourceType": { + "enum": [ + "relationshipModels" + ], + "type": "string" + }, + "relationshipModelSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "toManyRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "toOneRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "additionalProperties": false + }, + "toOneRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs new file mode 100644 index 0000000000..a4d64afdbe --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode; + +internal partial class NullableReferenceTypesEnabledClientRelationshipsObject : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs new file mode 100644 index 0000000000..a1d331c40a --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs @@ -0,0 +1,487 @@ +using System.Net; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +public sealed class RequestTests +{ + private const string NrtEnabledModelUrl = "http://localhost/nrtEnabledModels"; + + [Fact] + public async Task Can_exclude_optional_relationships() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtEnabledModelPostRequestDocument() + { + Data = new NrtEnabledModelDataInPostRequest + { + Relationships = new NrtEnabledModelRelationshipsInPostRequest + { + HasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + NullableRequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtEnabledModels"", + ""relationships"": { + ""hasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""nullableRequiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.HasOne), "hasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.NullableRequiredHasOne), "nullableRequiredHasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + + NrtEnabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + NullableRequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtEnabledModelPostRequestDocument + { + Data = new NrtEnabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } + + [Theory] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.HasOne), "hasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.NullableRequiredHasOne), "nullableRequiredHasOne")] + [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + + NrtEnabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + { + HasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + NullableHasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + NullableRequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new NrtEnabledModelPostRequestDocument + { + Data = new NrtEnabledModelDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtEnabledModelPatchRequestDocument() + { + Data = new NrtEnabledModelDataInPatchRequest + { + Id = "1", + Type = NrtEnabledModelResourceType.NrtEnabledModels, + Relationships = new NrtEnabledModelRelationshipsInPatchRequest() + { + NullableHasOne = new NullableToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtEnabledModelAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtEnabledModels"", + ""id"": ""1"", + ""relationships"": { + ""nullableHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Fact] + public async Task Can_clear_nullable_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + + var requestDocument = new NrtEnabledModelPostRequestDocument() + { + Data = new NrtEnabledModelDataInPostRequest + { + Relationships = new NrtEnabledModelRelationshipsInPostRequest + { + NullableHasOne = new NullableToOneRelationshipModelInRequest + { + Data = null + }, + HasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + RequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + NullableRequiredHasOne = new ToOneRelationshipModelInRequest + { + Data = new RelationshipModelIdentifier + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + }, + HasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + }, + RequiredHasMany = new ToManyRelationshipModelInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = RelationshipModelResourceType.RelationshipModels + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""nrtEnabledModels"", + ""relationships"": { + ""hasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""requiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""nullableHasOne"": { + ""data"": null + }, + ""nullableRequiredHasOne"": { + ""data"": { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + }, + ""hasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + }, + ""requiredHasMany"": { + ""data"": [ + { + ""type"": ""relationshipModels"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json new file mode 100644 index 0000000000..7522cd4fee --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json @@ -0,0 +1,1954 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/nrtEnabledModels": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "postNrtEnabledModel", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "deleteNrtEnabledModel", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/hasMany": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/hasMany": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "postNrtEnabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "deleteNrtEnabledModelHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/hasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/hasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/nullableHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelNullableHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelNullableHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/nullableHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelNullableHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelNullableHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelNullableHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/nullableRequiredHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelNullableRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelNullableRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/nullableRequiredHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelNullableRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelNullableRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelNullableRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/requiredHasMany": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelRequiredHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelRequiredHasMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/requiredHasMany": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "postNrtEnabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "deleteNrtEnabledModelRequiredHasManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/nrtEnabledModels/{id}/requiredHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelRequiredHasOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/nrtEnabledModels/{id}/relationships/requiredHasOne": { + "get": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "getNrtEnabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "headNrtEnabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "nrtEnabledModels" + ], + "operationId": "patchNrtEnabledModelRequiredHasOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "components": { + "schemas": { + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nrtEnabledModelDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtEnabledModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "relationships": { + "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtEnabledModelResourceType" + }, + "relationships": { + "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/nrtEnabledModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "relationships": { + "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nrtEnabledModelPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtEnabledModelDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtEnabledModelDataInPostRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/nrtEnabledModelDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "nullableHasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + }, + "nullableRequiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelRelationshipsInPostRequest": { + "required": [ + "hasOne", + "nullableRequiredHasOne", + "requiredHasMany", + "requiredHasOne" + ], + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "nullableHasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" + }, + "nullableRequiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInRequest" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInRequest" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelRelationshipsInResponse": { + "type": "object", + "properties": { + "hasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInResponse" + }, + "requiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInResponse" + }, + "nullableHasOne": { + "$ref": "#/components/schemas/nullableToOneRelationshipModelInResponse" + }, + "nullableRequiredHasOne": { + "$ref": "#/components/schemas/toOneRelationshipModelInResponse" + }, + "hasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInResponse" + }, + "requiredHasMany": { + "$ref": "#/components/schemas/toManyRelationshipModelInResponse" + } + }, + "additionalProperties": false + }, + "nrtEnabledModelResourceType": { + "enum": [ + "nrtEnabledModels" + ], + "type": "string" + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullableRelationshipModelIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableRelationshipModelSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "relationshipModelCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/relationshipModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/relationshipModelResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "relationshipModelResourceType": { + "enum": [ + "relationshipModels" + ], + "type": "string" + }, + "relationshipModelSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "toManyRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "toOneRelationshipModelInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + } + }, + "additionalProperties": false + }, + "toOneRelationshipModelInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/relationshipModelIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiTests/JsonElementExtensions.cs b/test/OpenApiTests/JsonElementExtensions.cs index 6f1ffe1432..93d903baac 100644 --- a/test/OpenApiTests/JsonElementExtensions.cs +++ b/test/OpenApiTests/JsonElementExtensions.cs @@ -2,6 +2,7 @@ using BlushingPenguin.JsonPath; using FluentAssertions; using FluentAssertions.Execution; +using JetBrains.Annotations; using TestBuildingBlocks; namespace OpenApiTests; @@ -33,6 +34,14 @@ public static void ShouldBeString(this JsonElement source, string value) } public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElement source, string value) + { + string schemaReferenceId = GetSchemaReferenceId(source); + schemaReferenceId.Should().Be(value); + + return new SchemaReferenceIdContainer(value); + } + + public static string GetSchemaReferenceId(this JsonElement source) { source.ValueKind.Should().Be(JsonValueKind.String); @@ -40,9 +49,14 @@ public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElem jsonElementValue.ShouldNotBeNull(); string schemaReferenceId = jsonElementValue.Split('/').Last(); - schemaReferenceId.Should().Be(value); + return schemaReferenceId; + } - return new SchemaReferenceIdContainer(value); + public static void WithSchemaReferenceId(this JsonElement subject, [InstantHandle] Action continuation) + { + string schemaReferenceId = GetSchemaReferenceId(subject); + + continuation(schemaReferenceId); } public sealed class SchemaReferenceIdContainer @@ -84,3 +98,4 @@ public void ContainProperty(string propertyName) } } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs new file mode 100644 index 0000000000..f34db02848 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +public sealed class ModelStateValidationDisabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("requiredHasOne")] + [InlineData("requiredHasMany")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("hasOne")] + [InlineData("hasMany")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.nrtDisabledModelRelationshipsInPatchRequest.required"); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..d24f7e63c8 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs @@ -0,0 +1,65 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("requiredHasOne")] + [InlineData("requiredHasMany")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("hasOne")] + [InlineData("hasMany")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.nrtDisabledModelRelationshipsInPatchRequest.required"); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs new file mode 100644 index 0000000000..b23e1e10ff --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +#nullable disable + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class NrtDisabledModel : Identifiable +{ + [HasOne] public RelationshipModel HasOne { get; set; } + [Required] [HasOne] public RelationshipModel RequiredHasOne { get; set; } + [HasMany] public ICollection HasMany { get; set; } = new HashSet(); + [Required] [HasMany] public ICollection RequiredHasMany { get; set; } = new HashSet(); +} \ No newline at end of file diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs new file mode 100644 index 0000000000..24f31d855a --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs @@ -0,0 +1,56 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject"; + } + + [Theory] + [InlineData("hasOne")] + public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("hasMany")] + [InlineData("requiredHasOne")] + [InlineData("requiredHasMany")] + public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs new file mode 100644 index 0000000000..e0a777d8a5 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +// @formatter:wrap_chained_method_calls chop_always + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext +{ + public DbSet NrtDisabledModel => Set(); + public DbSet RelationshipModel => Set(); + + public NullableReferenceTypesDisabledDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.HasOne); + builder.Entity() + .HasOne(resource => resource.RequiredHasOne); + builder.Entity() + .HasMany(resource => resource.HasMany); + builder.Entity() + .HasMany(resource => resource.RequiredHasMany); + + base.OnModelCreating(builder); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs new file mode 100644 index 0000000000..5bb9e96563 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs @@ -0,0 +1,10 @@ +#nullable disable +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +[Resource] +public class RelationshipModel : Identifiable +{ +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index 3b8d0597d7..be3c930744 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -21,7 +21,7 @@ public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("requiredHasOne")] + [InlineData("requiredHasMany")] + [InlineData("nullableRequiredHasOne")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("hasOne")] + [InlineData("hasMany")] + [InlineData("nullableHasOne")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.nrtEnabledModelRelationshipsInPatchRequest.required"); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..174bac59aa --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("hasOne")] + [InlineData("requiredHasOne")] + [InlineData("requiredHasMany")] + [InlineData("nullableRequiredHasOne")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("hasMany")] + [InlineData("nullableHasOne")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.nrtEnabledModelRelationshipsInPatchRequest.required"); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs new file mode 100644 index 0000000000..109d4510c2 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class NrtEnabledModel : Identifiable +{ + [HasOne] + public RelationshipModel HasOne { get; set; } = null!; + + [Required] + [HasOne] + public RelationshipModel RequiredHasOne { get; set; } = null!; + + [HasOne] + public RelationshipModel? NullableHasOne { get; set; } + + [Required] + [HasOne] + public RelationshipModel? NullableRequiredHasOne { get; set; } + + [HasMany] + public ICollection HasMany { get; set; } = new HashSet(); + + [Required] + [HasMany] + public ICollection RequiredHasMany { get; set; } = new HashSet(); +} \ No newline at end of file diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs new file mode 100644 index 0000000000..ed710bbf41 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs @@ -0,0 +1,58 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject"; + } + + [Theory] + [InlineData("nullableHasOne")] + public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("hasOne")] + [InlineData("requiredHasOne")] + [InlineData("hasMany")] + [InlineData("requiredHasMany")] + [InlineData("nullableRequiredHasOne")] + public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs new file mode 100644 index 0000000000..0979fe4e9a --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs @@ -0,0 +1,43 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +// @formatter:wrap_chained_method_calls chop_always + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext +{ + public DbSet NrtEnabledModel => Set(); + public DbSet RelationshipModel => Set(); + + public NullableReferenceTypesEnabledDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.HasOne); + + builder.Entity() + .HasOne(resource => resource.RequiredHasOne); + + builder.Entity() + .HasMany(resource => resource.HasMany); + + builder.Entity() + .HasMany(resource => resource.RequiredHasMany); + + builder.Entity() + .HasOne(resource => resource.NullableHasOne); + + builder.Entity() + .HasOne(resource => resource.NullableRequiredHasOne); + + base.OnModelCreating(builder); + } +} + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs new file mode 100644 index 0000000000..119cb1e9e3 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs @@ -0,0 +1,9 @@ +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +[Resource] +public class RelationshipModel : Identifiable +{ +} From 90970a844d75eb368718f80a182d6ae0f1d7c964 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Sat, 24 Dec 2022 19:00:28 +0100 Subject: [PATCH 18/66] - Rename placeholder model names and properties to examples consisent with existing test suite - Use existing DbContext instead of temporary one --- .../OpenApiClientTests.csproj | 14 - .../NullableReferenceTypesDisabledClient.cs | 1 + .../NullableReferenceTypesDisabledClient.cs | 14 - .../RelationshipsObject/RequestTests.cs | 315 +-- .../RelationshipsObject/RequestTestsAlt.cs | 351 ++- .../RelationshipsObject/swagger.g.json | 1632 -------------- .../RequestTests.cs | 3 +- .../swagger.g.json | 1542 ++++++++++++- .../NullableReferenceTypesEnabledClient.cs | 14 - .../RelationshipsObject/RequestTests.cs | 288 +-- .../RelationshipsObject/swagger.g.json | 1954 ----------------- .../swagger.g.json | 1856 +++++++++++++++- .../NullabilityTests.cs | 2 + ...NullableReferenceTypesDisabledDbContext.cs | 21 + .../RelationshipsObject/HenHouse.cs | 27 + .../ModelStateValidationDisabledTests.cs | 17 +- .../ModelStateValidationEnabledTests.cs | 17 +- .../RelationshipsObject/NrtDisabledModel.cs | 18 - .../RelationshipsObject/NullabilityTests.cs | 16 +- ...NullableReferenceTypesDisabledDbContext.cs | 33 - .../RelationshipsObject/RelationshipModel.cs | 10 - .../NullableReferenceTypesEnabled/Cow.cs | 2 + .../NullabilityTests.cs | 2 + .../NullableReferenceTypesEnabledDbContext.cs | 28 + .../RelationshipsObject/CowStable.cs | 32 + .../ModelStateValidationDisabledTests.cs | 21 +- .../ModelStateValidationEnabledTests.cs | 21 +- .../RelationshipsObject/NrtEnabledModel.cs | 32 - .../RelationshipsObject/NullabilityTests.cs | 20 +- .../NullableReferenceTypesEnabledDbContext.cs | 43 - .../RelationshipsObject/RelationshipModel.cs | 9 - 31 files changed, 3990 insertions(+), 4365 deletions(-) delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs create mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 143668412b..badeeaeae7 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -66,19 +66,5 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - - OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode - NullableReferenceTypesEnabledClientRelationshipsObject - NullableReferenceTypesEnabledClientRelationshipsObject.cs - NSwagCSharp - /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - - - OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode - NullableReferenceTypesDisabledClientRelationshipsObject - NullableReferenceTypesDisabledClientRelationshipsObject.cs - NSwagCSharp - /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs index c1a4eebab5..6670a0566f 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs @@ -12,3 +12,4 @@ partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) settings.Formatting = Formatting.Indented; } } + diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs deleted file mode 100644 index 40ab429277..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesDisabledClient.cs +++ /dev/null @@ -1,14 +0,0 @@ -using JsonApiDotNetCore.OpenApi.Client; -using Newtonsoft.Json; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; - -internal partial class NullableReferenceTypesDisabledClientRelationshipsObject : JsonApiClient -{ - partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) - { - SetSerializerSettingsForJsonApi(settings); - - settings.Formatting = Formatting.Indented; - } -} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs index 1dea915b88..9ccda82859 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs @@ -5,45 +5,45 @@ using JsonApiDotNetCore.Middleware; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class RequestTests { - private const string NrtDisabledModelUrl = "http://localhost/nrtDisabledModels"; + private const string HenHouseUrl = "http://localhost/henHouses"; [Fact] public async Task Can_exclude_optional_relationships() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - var requestDocument = new NrtDisabledModelPostRequestDocument() + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { - Relationships = new NrtDisabledModelRelationshipsInPostRequest + Relationships = new HenHouseRelationshipsInPostRequest { - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -51,31 +51,31 @@ public async Task Can_exclude_optional_relationships() } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", + ""type"": ""henHouses"", ""relationships"": { - ""requiredHasOne"": { + ""firstChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }, - ""requiredHasMany"": { + ""chickensReadyForLaying"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -86,51 +86,51 @@ public async Task Can_exclude_optional_relationships() } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -138,75 +138,74 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) { // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); - } - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -214,17 +213,17 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -238,32 +237,32 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - var requestDocument = new NrtDisabledModelPatchRequestDocument() + var requestDocument = new HenHousePatchRequestDocument { - Data = new NrtDisabledModelDataInPatchRequest + Data = new HenHouseDataInPatchRequest { Id = "1", - Type = NrtDisabledModelResourceType.NrtDisabledModels, - Relationships = new NrtDisabledModelRelationshipsInPatchRequest() + Type = HenHouseResourceType.HenHouses, + Relationships = new HenHouseRelationshipsInPatchRequest { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -271,32 +270,32 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtDisabledModelAsync(1, requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl + "/1"); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", + ""type"": ""henHouses"", ""id"": ""1"", ""relationships"": { - ""hasOne"": { + ""oldestChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }, - ""hasMany"": { + ""allChickens"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -311,45 +310,45 @@ public async Task Can_clear_nullable_relationship() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - var requestDocument = new NrtDisabledModelPostRequestDocument() + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { - Relationships = new NrtDisabledModelRelationshipsInPostRequest + Relationships = new HenHouseRelationshipsInPostRequest { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { Data = null }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -357,42 +356,42 @@ public async Task Can_clear_nullable_relationship() } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", + ""type"": ""henHouses"", ""relationships"": { - ""hasOne"": { + ""oldestChicken"": { ""data"": null }, - ""requiredHasOne"": { + ""firstChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }, - ""hasMany"": { + ""allChickens"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] }, - ""requiredHasMany"": { + ""chickensReadyForLaying"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -403,52 +402,52 @@ public async Task Can_clear_nullable_relationship() } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -458,76 +457,77 @@ public async Task Cannot_clear_non_nullable_relationships_with_document_registra object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; relationshipToClear.SetPropertyToDefaultValue("Data"); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, model => model.RequiredHasOne, model => model.HasMany, model => model.RequiredHasMany)) + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) { // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); } - + // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); } - + [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -537,17 +537,17 @@ public async Task Cannot_clear_non_nullable_relationships_without_document_regis object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; relationshipToClear.SetPropertyToDefaultValue("Data"); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - + // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -556,3 +556,4 @@ public async Task Cannot_clear_non_nullable_relationships_without_document_regis exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); } } + diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs index 8dd29c3fb7..870bb548fd 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs @@ -5,40 +5,39 @@ using JsonApiDotNetCore.Middleware; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject.GeneratedCode; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class RequestTestsAlt { - private const string NrtDisabledModelUrl = "http://localhost/nrtDisabledModels"; + private const string HenHouseUrl = "http://localhost/henHouses"; private readonly Dictionary _partials = new() { { - nameof(NrtDisabledModelRelationshipsInPostRequest.HasOne), @"""hasOne"": { + nameof(HenHouseRelationshipsInPostRequest.OldestChicken), @"""oldestChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }" }, { - nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), @"""requiredHasOne"": { + nameof(HenHouseRelationshipsInPostRequest.FirstChicken), @"""firstChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }" }, - { - nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), @"""hasMany"": { + nameof(HenHouseRelationshipsInPostRequest.AllChickens), @"""allChickens"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -46,10 +45,10 @@ public sealed class RequestTestsAlt }, { - nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), @"""requiredHasMany"": { + nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), @"""chickensReadyForLaying"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -58,51 +57,51 @@ public sealed class RequestTestsAlt }; [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasOne))] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.OldestChicken))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens))] public async Task Can_exclude_optional_relationships(string propertyName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsObject = new() + HenHouseRelationshipsInPostRequest relationshipsObject = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -110,22 +109,21 @@ public async Task Can_exclude_optional_relationships(string propertyName) relationshipsObject.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsObject } }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -134,58 +132,58 @@ public async Task Can_exclude_optional_relationships(string propertyName) wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", - ""relationships"": "+ body +@" + ""type"": ""henHouses"", + ""relationships"": " + body + @" } }"); } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -193,75 +191,74 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) { // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); - } + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -269,17 +266,17 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -289,51 +286,51 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne))] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying))] public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH(string propertyName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - var relationshipsObject = new NrtDisabledModelRelationshipsInPatchRequest() + var relationshipsObject = new HenHouseRelationshipsInPatchRequest { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -341,23 +338,23 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf relationshipsObject.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtDisabledModelPatchRequestDocument() + var requestDocument = new HenHousePatchRequestDocument { - Data = new NrtDisabledModelDataInPatchRequest + Data = new HenHouseDataInPatchRequest { Id = "1", - Type = NrtDisabledModelResourceType.NrtDisabledModels, + Type = HenHouseResourceType.HenHouses, Relationships = relationshipsObject } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtDisabledModelAsync(1, requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl + "/1"); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -366,9 +363,9 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", + ""type"": ""henHouses"", ""id"": ""1"", - ""relationships"": "+ serializedRelationshipsObject +@" + ""relationships"": " + serializedRelationshipsObject + @" } }"); } @@ -378,45 +375,45 @@ public async Task Can_clear_nullable_relationship() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - var requestDocument = new NrtDisabledModelPostRequestDocument() + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { - Relationships = new NrtDisabledModelRelationshipsInPostRequest + Relationships = new HenHouseRelationshipsInPostRequest { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { Data = null }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -424,42 +421,42 @@ public async Task Can_clear_nullable_relationship() } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtDisabledModelUrl); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtDisabledModels"", + ""type"": ""henHouses"", ""relationships"": { - ""hasOne"": { + ""oldestChicken"": { ""data"": null }, - ""requiredHasOne"": { + ""firstChicken"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } }, - ""hasMany"": { + ""allChickens"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] }, - ""requiredHasMany"": { + ""chickensReadyForLaying"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""chickens"", ""id"": ""1"" } ] @@ -470,52 +467,52 @@ public async Task Can_clear_nullable_relationship() } [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -525,19 +522,20 @@ public async Task Cannot_clear_non_nullable_relationships_with_document_registra object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; relationshipToClear.SetPropertyToDefaultValue("Data"); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, model => model.RequiredHasOne, model => model.HasMany, model => model.RequiredHasMany)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -546,54 +544,54 @@ public async Task Cannot_clear_non_nullable_relationships_with_document_registra exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); } } - + [Theory] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.HasMany), "hasMany")] - [InlineData(nameof(NrtDisabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - NrtDisabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new NullableToOneRelationshipModelInRequest + OldestChicken = new NullableToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstChicken = new ToOneChickenInRequest { - Data = new RelationshipModelIdentifier + Data = new ChickenIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } }, - HasMany = new ToManyRelationshipModelInRequest + AllChickens = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + ChickensReadyForLaying = new ToManyChickenInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = ChickenResourceType.Chickens } } } @@ -603,17 +601,17 @@ public async Task Cannot_clear_non_nullable_relationships_without_document_regis object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; relationshipToClear.SetPropertyToDefaultValue("Data"); - var requestDocument = new NrtDisabledModelPostRequestDocument + var requestDocument = new HenHousePostRequestDocument { - Data = new NrtDisabledModelDataInPostRequest + Data = new HenHouseDataInPostRequest { Relationships = relationshipsInPostDocument } }; - + // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtDisabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -621,7 +619,7 @@ public async Task Cannot_clear_non_nullable_relationships_without_document_regis exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); } - + private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludeProperty) { string partial = ""; @@ -631,7 +629,7 @@ private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludePro if (excludeProperty == key) { continue; - } + } if (partial.Length > 0) { @@ -646,3 +644,4 @@ private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludePro }"; } } + diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json deleted file mode 100644 index 2d92b99038..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/swagger.g.json +++ /dev/null @@ -1,1632 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "OpenApiTests", - "version": "1.0" - }, - "paths": { - "/nrtDisabledModels": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "postNrtDisabledModel", - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPostRequestDocument" - } - } - } - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "No Content" - } - } - } - }, - "/nrtDisabledModels/{id}": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "patchNrtDisabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPatchRequestDocument" - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtDisabledModelPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "deleteNrtDisabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtDisabledModels/{id}/hasMany": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - } - }, - "/nrtDisabledModels/{id}/relationships/hasMany": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "postNrtDisabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "patch": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "patchNrtDisabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "deleteNrtDisabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtDisabledModels/{id}/hasOne": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtDisabledModels/{id}/relationships/hasOne": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "patchNrtDisabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtDisabledModels/{id}/requiredHasMany": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelRequiredHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelRequiredHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - } - }, - "/nrtDisabledModels/{id}/relationships/requiredHasMany": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "postNrtDisabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "patch": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "patchNrtDisabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "deleteNrtDisabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtDisabledModels/{id}/requiredHasOne": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtDisabledModels/{id}/relationships/requiredHasOne": { - "get": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "getNrtDisabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "headNrtDisabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtDisabledModels" - ], - "operationId": "patchNrtDisabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - } - }, - "components": { - "schemas": { - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "linksInRelationshipObject": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceCollectionDocument": { - "required": [ - "first", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "first": { - "minLength": 1, - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceDocument": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceIdentifierCollectionDocument": { - "required": [ - "first", - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - }, - "first": { - "minLength": 1, - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceIdentifierDocument": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceObject": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/nrtDisabledModelDataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtDisabledModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInPatchRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelDataInPostRequest": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtDisabledModelResourceType" - }, - "relationships": { - "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInPostRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtDisabledModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/nrtDisabledModelRelationshipsInResponse" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "nrtDisabledModelPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtDisabledModelDataInPatchRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelPostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtDisabledModelDataInPostRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelPrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtDisabledModelDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelRelationshipsInPostRequest": { - "required": [ - "requiredHasMany", - "requiredHasOne" - ], - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelRelationshipsInResponse": { - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInResponse" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInResponse" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInResponse" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInResponse" - } - }, - "additionalProperties": false - }, - "nrtDisabledModelResourceType": { - "enum": [ - "nrtDisabledModels" - ], - "type": "string" - }, - "nullValue": { - "not": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "object" - }, - { - "type": "array" - } - ], - "items": { } - }, - "nullable": true - }, - "nullableRelationshipModelIdentifierResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" - } - }, - "additionalProperties": false - }, - "nullableRelationshipModelSecondaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "nullableToOneRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - } - }, - "additionalProperties": false - }, - "nullableToOneRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "relationshipModelCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/relationshipModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifier": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/relationshipModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifierCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifierResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelResourceType": { - "enum": [ - "relationshipModels" - ], - "type": "string" - }, - "relationshipModelSecondaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "toManyRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - } - }, - "additionalProperties": false - }, - "toManyRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "toOneRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "additionalProperties": false - }, - "toOneRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - } - } - } -} \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs index b624a756ef..8d62fa4fad 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; -public sealed class RequestTests +public sealed class RelationshipRequestTests { private const string ChickenUrl = "http://localhost/chickens"; @@ -328,3 +328,4 @@ public async Task Can_set_default_value_to_ValueType_attributes() }"); } } + diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json index d71423c997..b694521108 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json @@ -195,6 +195,925 @@ } } } + }, + "/henHouses": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHouseCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHouseCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "henHouses" + ], + "operationId": "postHenHouse", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/henHouses/{id}": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouse", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouse", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "henHouses" + ], + "operationId": "patchHenHouse", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "henHouses" + ], + "operationId": "deleteHenHouse", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/henHouses/{id}/allChickens": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseAllChickens", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseAllChickens", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + } + }, + "/henHouses/{id}/relationships/allChickens": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseAllChickensRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseAllChickensRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "henHouses" + ], + "operationId": "postHenHouseAllChickensRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "henHouses" + ], + "operationId": "patchHenHouseAllChickensRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "henHouses" + ], + "operationId": "deleteHenHouseAllChickensRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/henHouses/{id}/chickensReadyForLaying": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseChickensReadyForLaying", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseChickensReadyForLaying", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenCollectionResponseDocument" + } + } + } + } + } + } + }, + "/henHouses/{id}/relationships/chickensReadyForLaying": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseChickensReadyForLayingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseChickensReadyForLayingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "henHouses" + ], + "operationId": "postHenHouseChickensReadyForLayingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "henHouses" + ], + "operationId": "patchHenHouseChickensReadyForLayingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "henHouses" + ], + "operationId": "deleteHenHouseChickensReadyForLayingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/henHouses/{id}/firstChicken": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseFirstChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseFirstChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/henHouses/{id}/relationships/firstChicken": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseFirstChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseFirstChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/chickenIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "henHouses" + ], + "operationId": "patchHenHouseFirstChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/henHouses/{id}/oldestChicken": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseOldestChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableChickenSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseOldestChicken", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableChickenSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/henHouses/{id}/relationships/oldestChicken": { + "get": { + "tags": [ + "henHouses" + ], + "operationId": "getHenHouseOldestChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableChickenIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "henHouses" + ], + "operationId": "headHenHouseOldestChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableChickenIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "henHouses" + ], + "operationId": "patchHenHouseOldestChickenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneChickenInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } } }, "components": { @@ -283,18 +1202,249 @@ "type": "integer", "format": "int32" }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true + "timeAtCurrentFarmInDays": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hasProducedEggs": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "chickenCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chickenDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "chickenDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInPatchRequest" + } + }, + "additionalProperties": false + }, + "chickenDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInPostRequest" + } + }, + "additionalProperties": false + }, + "chickenDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/chickenAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "chickenIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/chickenResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "chickenIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chickenIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "chickenIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "chickenPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "chickenPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInPostRequest" + } + }, + "additionalProperties": false + }, + "chickenPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "chickenResourceType": { + "enum": [ + "chickens" + ], + "type": "string" + }, + "chickenSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" }, - "hasProducedEggs": { - "type": "boolean" + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" } }, "additionalProperties": false }, - "chickenCollectionResponseDocument": { + "henHouseCollectionResponseDocument": { "required": [ "data", "links" @@ -304,12 +1454,12 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/chickenDataInResponse" + "$ref": "#/components/schemas/henHouseDataInResponse" } }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -320,7 +1470,7 @@ }, "additionalProperties": false }, - "chickenDataInPatchRequest": { + "henHouseDataInPatchRequest": { "required": [ "id", "type" @@ -328,34 +1478,34 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/chickenResourceType" + "$ref": "#/components/schemas/henHouseResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInPatchRequest" + "relationships": { + "$ref": "#/components/schemas/henHouseRelationshipsInPatchRequest" } }, "additionalProperties": false }, - "chickenDataInPostRequest": { + "henHouseDataInPostRequest": { "required": [ "type" ], "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/chickenResourceType" + "$ref": "#/components/schemas/henHouseResourceType" }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInPostRequest" + "relationships": { + "$ref": "#/components/schemas/henHouseRelationshipsInPostRequest" } }, "additionalProperties": false }, - "chickenDataInResponse": { + "henHouseDataInResponse": { "required": [ "id", "links", @@ -364,50 +1514,50 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/chickenResourceType" + "$ref": "#/components/schemas/henHouseResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInResponse" + "relationships": { + "$ref": "#/components/schemas/henHouseRelationshipsInResponse" }, "links": { "$ref": "#/components/schemas/linksInResourceObject" }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} } }, "additionalProperties": false }, - "chickenPatchRequestDocument": { + "henHousePatchRequestDocument": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenDataInPatchRequest" + "$ref": "#/components/schemas/henHouseDataInPatchRequest" } }, "additionalProperties": false }, - "chickenPostRequestDocument": { + "henHousePostRequestDocument": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenDataInPostRequest" + "$ref": "#/components/schemas/henHouseDataInPostRequest" } }, "additionalProperties": false }, - "chickenPrimaryResponseDocument": { + "henHousePrimaryResponseDocument": { "required": [ "data", "links" @@ -415,11 +1565,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenDataInResponse" + "$ref": "#/components/schemas/henHouseDataInResponse" }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -430,9 +1580,67 @@ }, "additionalProperties": false }, - "chickenResourceType": { + "henHouseRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "oldestChicken": { + "$ref": "#/components/schemas/nullableToOneChickenInRequest" + }, + "firstChicken": { + "$ref": "#/components/schemas/toOneChickenInRequest" + }, + "allChickens": { + "$ref": "#/components/schemas/toManyChickenInRequest" + }, + "chickensReadyForLaying": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + }, + "additionalProperties": false + }, + "henHouseRelationshipsInPostRequest": { + "required": [ + "chickensReadyForLaying", + "firstChicken" + ], + "type": "object", + "properties": { + "oldestChicken": { + "$ref": "#/components/schemas/nullableToOneChickenInRequest" + }, + "firstChicken": { + "$ref": "#/components/schemas/toOneChickenInRequest" + }, + "allChickens": { + "$ref": "#/components/schemas/toManyChickenInRequest" + }, + "chickensReadyForLaying": { + "$ref": "#/components/schemas/toManyChickenInRequest" + } + }, + "additionalProperties": false + }, + "henHouseRelationshipsInResponse": { + "type": "object", + "properties": { + "oldestChicken": { + "$ref": "#/components/schemas/nullableToOneChickenInResponse" + }, + "firstChicken": { + "$ref": "#/components/schemas/toOneChickenInResponse" + }, + "allChickens": { + "$ref": "#/components/schemas/toManyChickenInResponse" + }, + "chickensReadyForLaying": { + "$ref": "#/components/schemas/toManyChickenInResponse" + } + }, + "additionalProperties": false + }, + "henHouseResourceType": { "enum": [ - "chickens" + "henHouses" ], "type": "string" }, @@ -456,7 +1664,25 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" } }, "additionalProperties": false @@ -507,6 +1733,62 @@ }, "additionalProperties": false }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, "linksInResourceObject": { "required": [ "self" @@ -519,6 +1801,202 @@ } }, "additionalProperties": false + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": {} + }, + "nullable": true + }, + "nullableChickenIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/chickenIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableChickenSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/chickenDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneChickenInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/chickenIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneChickenInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/chickenIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "toManyChickenInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chickenIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyChickenInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chickenIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "toOneChickenInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenIdentifier" + } + }, + "additionalProperties": false + }, + "toOneChickenInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/chickenIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false } } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs deleted file mode 100644 index a4d64afdbe..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/GeneratedCode/NullableReferenceTypesEnabledClient.cs +++ /dev/null @@ -1,14 +0,0 @@ -using JsonApiDotNetCore.OpenApi.Client; -using Newtonsoft.Json; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode; - -internal partial class NullableReferenceTypesEnabledClientRelationshipsObject : JsonApiClient -{ - partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) - { - SetSerializerSettingsForJsonApi(settings); - - settings.Formatting = Formatting.Indented; - } -} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs index a1d331c40a..02659fc39e 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs @@ -4,61 +4,61 @@ using JsonApiDotNetCore.Middleware; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject.GeneratedCode; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; -public sealed class RequestTests +public sealed class RelationshipsRequestTests { - private const string NrtEnabledModelUrl = "http://localhost/nrtEnabledModels"; + private const string CowStableUrl = "http://localhost/cowStables"; [Fact] public async Task Can_exclude_optional_relationships() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - var requestDocument = new NrtEnabledModelPostRequestDocument() + var requestDocument = new CowStablePostRequestDocument { - Data = new NrtEnabledModelDataInPostRequest + Data = new CowStableDataInPostRequest { - Relationships = new NrtEnabledModelRelationshipsInPostRequest + Relationships = new CowStableRelationshipsInPostRequest { - HasOne = new ToOneRelationshipModelInRequest + OldestCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - NullableRequiredHasOne = new ToOneRelationshipModelInRequest + FavoriteCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + AllCows = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } } @@ -66,43 +66,43 @@ public async Task Can_exclude_optional_relationships() } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl); + wrapper.Request.RequestUri.Should().Be(CowStableUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtEnabledModels"", + ""type"": ""cowStables"", ""relationships"": { - ""hasOne"": { + ""oldestCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""requiredHasOne"": { + ""firstCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""nullableRequiredHasOne"": { + ""favoriteCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""requiredHasMany"": { + ""allCows"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } ] @@ -113,61 +113,61 @@ public async Task Can_exclude_optional_relationships() } [Theory] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.HasOne), "hasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.NullableRequiredHasOne), "nullableRequiredHasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - NrtEnabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new ToOneRelationshipModelInRequest + OldestCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - NullableRequiredHasOne = new ToOneRelationshipModelInRequest + FavoriteCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - HasMany = new ToManyRelationshipModelInRequest + CowsReadyForMilking = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + AllCows = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } } @@ -175,93 +175,92 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtEnabledModelPostRequestDocument + var requestDocument = new CowStablePostRequestDocument { - Data = new NrtEnabledModelDataInPostRequest + Data = new CowStableDataInPostRequest { Relationships = relationshipsInPostDocument } }; - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) { // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); - } - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } } [Theory] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.HasOne), "hasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasOne), "requiredHasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.NullableRequiredHasOne), "nullableRequiredHasOne")] - [InlineData(nameof(NrtEnabledModelRelationshipsInPostRequest.RequiredHasMany), "requiredHasMany")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - NrtEnabledModelRelationshipsInPostRequest relationshipsInPostDocument = new() + CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() { - HasOne = new ToOneRelationshipModelInRequest + OldestCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - NullableHasOne = new NullableToOneRelationshipModelInRequest + AlbinoCow = new NullableToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - NullableRequiredHasOne = new ToOneRelationshipModelInRequest + FavoriteCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - HasMany = new ToManyRelationshipModelInRequest + CowsReadyForMilking = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + AllCows = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } } @@ -269,17 +268,17 @@ public async Task Cannot_exclude_required_relationship_when_performing_POST_with relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - var requestDocument = new NrtEnabledModelPostRequestDocument + var requestDocument = new CowStablePostRequestDocument { - Data = new NrtEnabledModelDataInPostRequest + Data = new CowStableDataInPostRequest { Relationships = relationshipsInPostDocument } }; // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -293,32 +292,32 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - var requestDocument = new NrtEnabledModelPatchRequestDocument() + var requestDocument = new CowStablePatchRequestDocument { - Data = new NrtEnabledModelDataInPatchRequest + Data = new CowStableDataInPatchRequest { Id = "1", - Type = NrtEnabledModelResourceType.NrtEnabledModels, - Relationships = new NrtEnabledModelRelationshipsInPatchRequest() + Type = CowStableResourceType.CowStables, + Relationships = new CowStableRelationshipsInPatchRequest { - NullableHasOne = new NullableToOneRelationshipModelInRequest + AlbinoCow = new NullableToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - HasMany = new ToManyRelationshipModelInRequest + CowsReadyForMilking = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } } @@ -326,32 +325,32 @@ public async Task Can_exclude_relationships_that_are_required_for_POST_when_perf } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PatchNrtEnabledModelAsync(1, requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(1, requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl + "/1"); + wrapper.Request.RequestUri.Should().Be(CowStableUrl + "/1"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtEnabledModels"", + ""type"": ""cowStables"", ""id"": ""1"", ""relationships"": { - ""nullableHasOne"": { + ""albinoCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""hasMany"": { + ""cowsReadyForMilking"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } ] @@ -366,61 +365,61 @@ public async Task Can_clear_nullable_relationship() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClientRelationshipsObject(wrapper.HttpClient); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - var requestDocument = new NrtEnabledModelPostRequestDocument() + var requestDocument = new CowStablePostRequestDocument { - Data = new NrtEnabledModelDataInPostRequest + Data = new CowStableDataInPostRequest { - Relationships = new NrtEnabledModelRelationshipsInPostRequest + Relationships = new CowStableRelationshipsInPostRequest { - NullableHasOne = new NullableToOneRelationshipModelInRequest + AlbinoCow = new NullableToOneCowInRequest { Data = null }, - HasOne = new ToOneRelationshipModelInRequest + OldestCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - RequiredHasOne = new ToOneRelationshipModelInRequest + FirstCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - NullableRequiredHasOne = new ToOneRelationshipModelInRequest + FavoriteCow = new ToOneCowInRequest { - Data = new RelationshipModelIdentifier + Data = new CowIdentifier { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } }, - HasMany = new ToManyRelationshipModelInRequest + CowsReadyForMilking = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } }, - RequiredHasMany = new ToManyRelationshipModelInRequest + AllCows = new ToManyCowInRequest { - Data = new List + Data = new List { new() { Id = "1", - Type = RelationshipModelResourceType.RelationshipModels + Type = CowResourceType.Cows } } } @@ -428,54 +427,54 @@ public async Task Can_clear_nullable_relationship() } }; - await ApiResponse.TranslateAsync(async () => await apiClient.PostNrtEnabledModelAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(NrtEnabledModelUrl); + wrapper.Request.RequestUri.Should().Be(CowStableUrl); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); wrapper.RequestBody.Should().BeJson(@"{ ""data"": { - ""type"": ""nrtEnabledModels"", + ""type"": ""cowStables"", ""relationships"": { - ""hasOne"": { + ""oldestCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""requiredHasOne"": { + ""firstCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""nullableHasOne"": { + ""albinoCow"": { ""data"": null }, - ""nullableRequiredHasOne"": { + ""favoriteCow"": { ""data"": { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } }, - ""hasMany"": { + ""cowsReadyForMilking"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } ] }, - ""requiredHasMany"": { + ""allCows"": { ""data"": [ { - ""type"": ""relationshipModels"", + ""type"": ""cows"", ""id"": ""1"" } ] @@ -485,3 +484,4 @@ public async Task Can_clear_nullable_relationship() }"); } } + diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json deleted file mode 100644 index 7522cd4fee..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/swagger.g.json +++ /dev/null @@ -1,1954 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "OpenApiTests", - "version": "1.0" - }, - "paths": { - "/nrtEnabledModels": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "postNrtEnabledModel", - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPostRequestDocument" - } - } - } - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPatchRequestDocument" - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nrtEnabledModelPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "deleteNrtEnabledModel", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/hasMany": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/hasMany": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "postNrtEnabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "deleteNrtEnabledModelHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/hasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/hasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/nullableHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelNullableHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelNullableHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/nullableHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelNullableHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelNullableHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableRelationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelNullableHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/nullableRequiredHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelNullableRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelNullableRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/nullableRequiredHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelNullableRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelNullableRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelNullableRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/requiredHasMany": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelRequiredHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelRequiredHasMany", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelCollectionResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/requiredHasMany": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierCollectionResponseDocument" - } - } - } - } - } - }, - "post": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "postNrtEnabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "deleteNrtEnabledModelRequiredHasManyRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/nrtEnabledModels/{id}/requiredHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelRequiredHasOne", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelSecondaryResponseDocument" - } - } - } - } - } - } - }, - "/nrtEnabledModels/{id}/relationships/requiredHasOne": { - "get": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "getNrtEnabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "headNrtEnabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/relationshipModelIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "nrtEnabledModels" - ], - "operationId": "patchNrtEnabledModelRequiredHasOneRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - } - } - }, - "components": { - "schemas": { - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "linksInRelationshipObject": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceCollectionDocument": { - "required": [ - "first", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "first": { - "minLength": 1, - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceDocument": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceIdentifierCollectionDocument": { - "required": [ - "first", - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - }, - "first": { - "minLength": 1, - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceIdentifierDocument": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceObject": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/nrtEnabledModelDataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtEnabledModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInPatchRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelDataInPostRequest": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtEnabledModelResourceType" - }, - "relationships": { - "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInPostRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/nrtEnabledModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/nrtEnabledModelRelationshipsInResponse" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "nrtEnabledModelPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtEnabledModelDataInPatchRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelPostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtEnabledModelDataInPostRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelPrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/nrtEnabledModelDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "nullableHasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - }, - "nullableRequiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelRelationshipsInPostRequest": { - "required": [ - "hasOne", - "nullableRequiredHasOne", - "requiredHasMany", - "requiredHasOne" - ], - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "nullableHasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInRequest" - }, - "nullableRequiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInRequest" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInRequest" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelRelationshipsInResponse": { - "type": "object", - "properties": { - "hasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInResponse" - }, - "requiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInResponse" - }, - "nullableHasOne": { - "$ref": "#/components/schemas/nullableToOneRelationshipModelInResponse" - }, - "nullableRequiredHasOne": { - "$ref": "#/components/schemas/toOneRelationshipModelInResponse" - }, - "hasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInResponse" - }, - "requiredHasMany": { - "$ref": "#/components/schemas/toManyRelationshipModelInResponse" - } - }, - "additionalProperties": false - }, - "nrtEnabledModelResourceType": { - "enum": [ - "nrtEnabledModels" - ], - "type": "string" - }, - "nullValue": { - "not": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "object" - }, - { - "type": "array" - } - ], - "items": { } - }, - "nullable": true - }, - "nullableRelationshipModelIdentifierResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" - } - }, - "additionalProperties": false - }, - "nullableRelationshipModelSecondaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "nullableToOneRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - } - }, - "additionalProperties": false - }, - "nullableToOneRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" - } - ] - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "relationshipModelCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/relationshipModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifier": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/relationshipModelResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifierCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelIdentifierResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" - } - }, - "additionalProperties": false - }, - "relationshipModelResourceType": { - "enum": [ - "relationshipModels" - ], - "type": "string" - }, - "relationshipModelSecondaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "toManyRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - } - }, - "additionalProperties": false - }, - "toManyRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "toOneRelationshipModelInRequest": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - } - }, - "additionalProperties": false - }, - "toOneRelationshipModelInResponse": { - "required": [ - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/relationshipModelIdentifier" - }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - } - } - } -} \ No newline at end of file diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json index 14669b5c84..d32dc3f54c 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json @@ -195,6 +195,1227 @@ } } } + }, + "/cowStables": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStableCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStableCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "cowStables" + ], + "operationId": "postCowStable", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStable", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStable", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStable", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "cowStables" + ], + "operationId": "deleteCowStable", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/albinoCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableAlbinoCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableCowSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableAlbinoCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableCowSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/albinoCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableAlbinoCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableCowIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableAlbinoCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableCowIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableAlbinoCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/allCows": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableAllCows", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableAllCows", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/allCows": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableAllCowsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableAllCowsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "cowStables" + ], + "operationId": "postCowStableAllCowsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableAllCowsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "cowStables" + ], + "operationId": "deleteCowStableAllCowsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/cowsReadyForMilking": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableCowsReadyForMilking", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableCowsReadyForMilking", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowCollectionResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/cowsReadyForMilking": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableCowsReadyForMilkingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableCowsReadyForMilkingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "cowStables" + ], + "operationId": "postCowStableCowsReadyForMilkingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableCowsReadyForMilkingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "cowStables" + ], + "operationId": "deleteCowStableCowsReadyForMilkingRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/favoriteCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableFavoriteCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableFavoriteCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/favoriteCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableFavoriteCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableFavoriteCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableFavoriteCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/firstCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableFirstCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableFirstCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/firstCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableFirstCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableFirstCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableFirstCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/cowStables/{id}/oldestCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableOldestCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableOldestCow", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/cowStables/{id}/relationships/oldestCow": { + "get": { + "tags": [ + "cowStables" + ], + "operationId": "getCowStableOldestCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "cowStables" + ], + "operationId": "headCowStableOldestCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/cowIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "cowStables" + ], + "operationId": "patchCowStableOldestCowRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneCowInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } } }, "components": { @@ -311,13 +1532,244 @@ "format": "int32", "nullable": true }, - "hasProducedMilk": { - "type": "boolean" + "hasProducedMilk": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "cowCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/cowDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "cowDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInPatchRequest" + } + }, + "additionalProperties": false + }, + "cowDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInPostRequest" + } + }, + "additionalProperties": false + }, + "cowDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/cowAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "cowIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/cowResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "cowIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/cowIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "cowIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "cowPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "cowPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInPostRequest" + } + }, + "additionalProperties": false + }, + "cowPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "cowResourceType": { + "enum": [ + "cows" + ], + "type": "string" + }, + "cowSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" } }, "additionalProperties": false }, - "cowCollectionResponseDocument": { + "cowStableCollectionResponseDocument": { "required": [ "data", "links" @@ -327,12 +1779,12 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/cowDataInResponse" + "$ref": "#/components/schemas/cowStableDataInResponse" } }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -343,7 +1795,7 @@ }, "additionalProperties": false }, - "cowDataInPatchRequest": { + "cowStableDataInPatchRequest": { "required": [ "id", "type" @@ -351,34 +1803,34 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/cowResourceType" + "$ref": "#/components/schemas/cowStableResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInPatchRequest" + "relationships": { + "$ref": "#/components/schemas/cowStableRelationshipsInPatchRequest" } }, "additionalProperties": false }, - "cowDataInPostRequest": { + "cowStableDataInPostRequest": { "required": [ "type" ], "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/cowResourceType" + "$ref": "#/components/schemas/cowStableResourceType" }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInPostRequest" + "relationships": { + "$ref": "#/components/schemas/cowStableRelationshipsInPostRequest" } }, "additionalProperties": false }, - "cowDataInResponse": { + "cowStableDataInResponse": { "required": [ "id", "links", @@ -387,50 +1839,50 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/cowResourceType" + "$ref": "#/components/schemas/cowStableResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInResponse" + "relationships": { + "$ref": "#/components/schemas/cowStableRelationshipsInResponse" }, "links": { "$ref": "#/components/schemas/linksInResourceObject" }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} } }, "additionalProperties": false }, - "cowPatchRequestDocument": { + "cowStablePatchRequestDocument": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowDataInPatchRequest" + "$ref": "#/components/schemas/cowStableDataInPatchRequest" } }, "additionalProperties": false }, - "cowPostRequestDocument": { + "cowStablePostRequestDocument": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowDataInPostRequest" + "$ref": "#/components/schemas/cowStableDataInPostRequest" } }, "additionalProperties": false }, - "cowPrimaryResponseDocument": { + "cowStablePrimaryResponseDocument": { "required": [ "data", "links" @@ -438,11 +1890,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowDataInResponse" + "$ref": "#/components/schemas/cowStableDataInResponse" }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -453,9 +1905,87 @@ }, "additionalProperties": false }, - "cowResourceType": { + "cowStableRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "oldestCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "firstCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "albinoCow": { + "$ref": "#/components/schemas/nullableToOneCowInRequest" + }, + "favoriteCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "cowsReadyForMilking": { + "$ref": "#/components/schemas/toManyCowInRequest" + }, + "allCows": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + }, + "additionalProperties": false + }, + "cowStableRelationshipsInPostRequest": { + "required": [ + "allCows", + "favoriteCow", + "firstCow", + "oldestCow" + ], + "type": "object", + "properties": { + "oldestCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "firstCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "albinoCow": { + "$ref": "#/components/schemas/nullableToOneCowInRequest" + }, + "favoriteCow": { + "$ref": "#/components/schemas/toOneCowInRequest" + }, + "cowsReadyForMilking": { + "$ref": "#/components/schemas/toManyCowInRequest" + }, + "allCows": { + "$ref": "#/components/schemas/toManyCowInRequest" + } + }, + "additionalProperties": false + }, + "cowStableRelationshipsInResponse": { + "type": "object", + "properties": { + "oldestCow": { + "$ref": "#/components/schemas/toOneCowInResponse" + }, + "firstCow": { + "$ref": "#/components/schemas/toOneCowInResponse" + }, + "albinoCow": { + "$ref": "#/components/schemas/nullableToOneCowInResponse" + }, + "favoriteCow": { + "$ref": "#/components/schemas/toOneCowInResponse" + }, + "cowsReadyForMilking": { + "$ref": "#/components/schemas/toManyCowInResponse" + }, + "allCows": { + "$ref": "#/components/schemas/toManyCowInResponse" + } + }, + "additionalProperties": false + }, + "cowStableResourceType": { "enum": [ - "cows" + "cowStables" ], "type": "string" }, @@ -479,7 +2009,25 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" } }, "additionalProperties": false @@ -530,6 +2078,62 @@ }, "additionalProperties": false }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, "linksInResourceObject": { "required": [ "self" @@ -542,6 +2146,202 @@ } }, "additionalProperties": false + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": {} + }, + "nullable": true + }, + "nullableCowIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/cowIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableCowSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/cowDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneCowInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/cowIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneCowInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/cowIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "toManyCowInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/cowIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyCowInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/cowIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "toOneCowInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowIdentifier" + } + }, + "additionalProperties": false + }, + "toOneCowInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/cowIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false } } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index a5485de6e5..7d79bf9e6d 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -15,6 +15,7 @@ public NullabilityTests(OpenApiTestContext(); + testContext.UseController(); testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled"; } @@ -56,3 +57,4 @@ public async Task Property_in_schema_for_resource_should_not_be_nullable(string }); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs index 16bdc07e15..57c3d96f17 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -1,16 +1,37 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; +// @formatter:wrap_chained_method_calls chop_always + [UsedImplicitly(ImplicitUseTargetFlags.Members)] public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext { public DbSet Chicken => Set(); + public DbSet HenHouse => Set(); public NullableReferenceTypesDisabledDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.OldestChicken); + + builder.Entity() + .HasOne(resource => resource.FirstChicken); + + builder.Entity() + .HasMany(resource => resource.AllChickens); + + builder.Entity() + .HasMany(resource => resource.ChickensReadyForLaying); + + base.OnModelCreating(builder); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs new file mode 100644 index 0000000000..b148444378 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs @@ -0,0 +1,27 @@ +#nullable disable + +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class HenHouse : Identifiable +{ + [HasOne] + public Chicken OldestChicken { get; set; } + + [Required] + [HasOne] + public Chicken FirstChicken { get; set; } + + [HasMany] + public ICollection AllChickens { get; set; } = new HashSet(); + + [Required] + [HasMany] + public ICollection ChickensReadyForLaying { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs index f34db02848..b9052eda76 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs @@ -16,19 +16,19 @@ public ModelStateValidationDisabledTests( { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("requiredHasOne")] - [InlineData("requiredHasMany")] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => { var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -37,15 +37,15 @@ public async Task Property_in_schema_for_relationships_in_POST_request_should_be } [Theory] - [InlineData("hasOne")] - [InlineData("hasMany")] + [InlineData("oldestChicken")] + [InlineData("allChickens")] public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => { var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -60,7 +60,8 @@ public async Task Schema_for_relationships_in_PATCH_request_should_have_no_requi JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.nrtDisabledModelRelationshipsInPatchRequest.required"); + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs index d24f7e63c8..9bea8d4062 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs @@ -15,19 +15,19 @@ public ModelStateValidationEnabledTests( { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("requiredHasOne")] - [InlineData("requiredHasMany")] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => { var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -36,15 +36,15 @@ public async Task Property_in_schema_for_relationships_in_POST_request_should_be } [Theory] - [InlineData("hasOne")] - [InlineData("hasMany")] + [InlineData("oldestChicken")] + [InlineData("allChickens")] public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => { var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -59,7 +59,8 @@ public async Task Schema_for_relationships_in_PATCH_request_should_have_no_requi JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.nrtDisabledModelRelationshipsInPatchRequest.required"); + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs deleted file mode 100644 index b23e1e10ff..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NrtDisabledModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -#nullable disable - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] -public sealed class NrtDisabledModel : Identifiable -{ - [HasOne] public RelationshipModel HasOne { get; set; } - [Required] [HasOne] public RelationshipModel RequiredHasOne { get; set; } - [HasMany] public ICollection HasMany { get; set; } = new HashSet(); - [Required] [HasMany] public ICollection RequiredHasMany { get; set; } = new HashSet(); -} \ No newline at end of file diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs index 24f31d855a..544c1cd9ec 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs @@ -13,19 +13,18 @@ public NullabilityTests(OpenApiTestContext(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject"; + testContext.UseController(); } [Theory] - [InlineData("hasOne")] + [InlineData("oldestChicken")] public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => { schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { @@ -35,16 +34,16 @@ public async Task Property_in_schema_for_relationship_of_resource_should_be_null } [Theory] - [InlineData("hasMany")] - [InlineData("requiredHasOne")] - [InlineData("requiredHasMany")] + [InlineData("allChickens")] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtDisabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => { schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { @@ -54,3 +53,4 @@ public async Task Property_in_schema_for_relationship_of_resource_should_not_be_ } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs deleted file mode 100644 index e0a777d8a5..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullableReferenceTypesDisabledDbContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -// @formatter:wrap_chained_method_calls chop_always - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext -{ - public DbSet NrtDisabledModel => Set(); - public DbSet RelationshipModel => Set(); - - public NullableReferenceTypesDisabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.HasOne); - builder.Entity() - .HasOne(resource => resource.RequiredHasOne); - builder.Entity() - .HasMany(resource => resource.HasMany); - builder.Entity() - .HasMany(resource => resource.RequiredHasMany); - - base.OnModelCreating(builder); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs deleted file mode 100644 index 5bb9e96563..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RelationshipModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -#nullable disable -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -[Resource] -public class RelationshipModel : Identifiable -{ -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs index 3b5562a0b3..f876351bec 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs @@ -37,3 +37,5 @@ public sealed class Cow : Identifiable [Required] public bool? HasProducedMilk { get; set; } } + + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index be3c930744..99100b8353 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -15,6 +15,7 @@ public NullabilityTests(OpenApiTestContext(); + testContext.UseController(); testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled"; } @@ -59,3 +60,4 @@ public async Task Property_in_schema_for_attribute_of_resource_should_not_be_nul } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs index b7011e7d27..278349c29a 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -1,16 +1,44 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; +using OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; +// @formatter:wrap_chained_method_calls chop_always + [UsedImplicitly(ImplicitUseTargetFlags.Members)] public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext { public DbSet Cow => Set(); + public DbSet CowStable => Set(); public NullableReferenceTypesEnabledDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.OldestCow); + + builder.Entity() + .HasOne(resource => resource.FirstCow); + + builder.Entity() + .HasOne(resource => resource.AlbinoCow); + + builder.Entity() + .HasOne(resource => resource.FavoriteCow); + + builder.Entity() + .HasMany(resource => resource.AllCows); + + builder.Entity() + .HasMany(resource => resource.CowsReadyForMilking); + + base.OnModelCreating(builder); + } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs new file mode 100644 index 0000000000..c2f4ad9367 --- /dev/null +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +public sealed class CowStable : Identifiable +{ + [HasOne] + public Cow OldestCow { get; set; } = null!; + + [Required] + [HasOne] + public Cow FirstCow { get; set; } = null!; + + [HasOne] + public Cow? AlbinoCow { get; set; } + + [Required] + [HasOne] + public Cow? FavoriteCow { get; set; } + + [HasMany] + public ICollection CowsReadyForMilking { get; set; } = new HashSet(); + + [Required] + [HasMany] + public ICollection AllCows { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs index b4ec24bbc0..f52097c764 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs @@ -16,20 +16,20 @@ public ModelStateValidationDisabledTests( { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("requiredHasOne")] - [InlineData("requiredHasMany")] - [InlineData("nullableRequiredHasOne")] + [InlineData("firstCow")] + [InlineData("allCows")] + [InlineData("favoriteCow")] public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => { var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -38,16 +38,16 @@ public async Task Property_in_schema_for_relationships_in_POST_request_should_be } [Theory] - [InlineData("hasOne")] - [InlineData("hasMany")] - [InlineData("nullableHasOne")] + [InlineData("oldestCow")] + [InlineData("cowsReadyForMilking")] + [InlineData("albinoCow")] public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => { var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -62,7 +62,8 @@ public async Task Schema_for_relationships_in_PATCH_request_should_have_no_requi JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.nrtEnabledModelRelationshipsInPatchRequest.required"); + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs index 174bac59aa..946e41ea27 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs @@ -15,21 +15,21 @@ public ModelStateValidationEnabledTests( { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("hasOne")] - [InlineData("requiredHasOne")] - [InlineData("requiredHasMany")] - [InlineData("nullableRequiredHasOne")] + [InlineData("oldestCow")] + [InlineData("firstCow")] + [InlineData("allCows")] + [InlineData("favoriteCow")] public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => { var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -38,15 +38,15 @@ public async Task Property_in_schema_for_relationships_in_POST_request_should_be } [Theory] - [InlineData("hasMany")] - [InlineData("nullableHasOne")] + [InlineData("cowsReadyForMilking")] + [InlineData("albinoCow")] public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.required").With(propertySet => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => { var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); @@ -61,7 +61,8 @@ public async Task Schema_for_relationships_in_PATCH_request_should_have_no_requi JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.nrtEnabledModelRelationshipsInPatchRequest.required"); + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs deleted file mode 100644 index 109d4510c2..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NrtEnabledModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] -public sealed class NrtEnabledModel : Identifiable -{ - [HasOne] - public RelationshipModel HasOne { get; set; } = null!; - - [Required] - [HasOne] - public RelationshipModel RequiredHasOne { get; set; } = null!; - - [HasOne] - public RelationshipModel? NullableHasOne { get; set; } - - [Required] - [HasOne] - public RelationshipModel? NullableRequiredHasOne { get; set; } - - [HasMany] - public ICollection HasMany { get; set; } = new HashSet(); - - [Required] - [HasMany] - public ICollection RequiredHasMany { get; set; } = new HashSet(); -} \ No newline at end of file diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs index ed710bbf41..ed599af06c 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs @@ -13,19 +13,18 @@ public NullabilityTests(OpenApiTestContext(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject"; + testContext.UseController(); } [Theory] - [InlineData("nullableHasOne")] + [InlineData("albinoCow")] public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => { schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { @@ -35,18 +34,18 @@ public async Task Property_in_schema_for_relationship_of_resource_should_be_null } [Theory] - [InlineData("hasOne")] - [InlineData("requiredHasOne")] - [InlineData("hasMany")] - [InlineData("requiredHasMany")] - [InlineData("nullableRequiredHasOne")] + [InlineData("oldestCow")] + [InlineData("firstCow")] + [InlineData("cowsReadyForMilking")] + [InlineData("allCows")] + [InlineData("favoriteCow")] public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.nrtEnabledModelRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => { schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { @@ -56,3 +55,4 @@ public async Task Property_in_schema_for_relationship_of_resource_should_not_be_ } } + diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs deleted file mode 100644 index 0979fe4e9a..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullableReferenceTypesEnabledDbContext.cs +++ /dev/null @@ -1,43 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -// @formatter:wrap_chained_method_calls chop_always - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext -{ - public DbSet NrtEnabledModel => Set(); - public DbSet RelationshipModel => Set(); - - public NullableReferenceTypesEnabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.HasOne); - - builder.Entity() - .HasOne(resource => resource.RequiredHasOne); - - builder.Entity() - .HasMany(resource => resource.HasMany); - - builder.Entity() - .HasMany(resource => resource.RequiredHasMany); - - builder.Entity() - .HasOne(resource => resource.NullableHasOne); - - builder.Entity() - .HasOne(resource => resource.NullableRequiredHasOne); - - base.OnModelCreating(builder); - } -} - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs deleted file mode 100644 index 119cb1e9e3..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RelationshipModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -[Resource] -public class RelationshipModel : Identifiable -{ -} From affc1876074fc9d6d5503bcf11ff4c63e0fa0614 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Sat, 24 Dec 2022 21:36:52 +0100 Subject: [PATCH 19/66] Move into consistent folder structure, remove bad cleanupcode eof linebreaks --- .../JsonApiClient.cs | 1 - .../ResourceFieldObjectSchemaBuilder.cs | 1 - .../AlternativeFormRequestTests.cs | 252 +++++++ .../NullableReferenceTypesDisabledClient.cs | 1 - .../RelationshipsObject/RequestTests.cs | 559 --------------- .../RelationshipsObject/RequestTestsAlt.cs | 647 ------------------ .../RequestTests.cs | 544 ++++++++++++++- .../swagger.g.json | 32 +- .../RelationshipsObject/RequestTests.cs | 487 ------------- .../RequestTests.cs | 471 +++++++++++++ .../swagger.g.json | 32 +- test/OpenApiTests/JsonElementExtensions.cs | 3 +- .../{RelationshipsObject => }/HenHouse.cs | 2 +- .../ModelStateValidationDisabledTests.cs | 45 ++ .../ModelStateValidationEnabledTests.cs | 45 ++ .../NullabilityTests.cs | 41 +- ...NullableReferenceTypesDisabledDbContext.cs | 1 - .../ModelStateValidationDisabledTests.cs | 67 -- .../ModelStateValidationEnabledTests.cs | 66 -- .../RelationshipsObject/NullabilityTests.cs | 56 -- .../NullableReferenceTypesEnabled/Cow.cs | 2 - .../{RelationshipsObject => }/CowStable.cs | 2 +- .../ModelStateValidationDisabledTests.cs | 47 ++ .../ModelStateValidationEnabledTests.cs | 47 ++ .../NullabilityTests.cs | 38 +- .../NullableReferenceTypesEnabledDbContext.cs | 2 - .../ModelStateValidationDisabledTests.cs | 69 -- .../ModelStateValidationEnabledTests.cs | 68 -- .../RelationshipsObject/NullabilityTests.cs | 58 -- 29 files changed, 1560 insertions(+), 2126 deletions(-) create mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs rename test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/{RelationshipsObject => }/HenHouse.cs (96%) delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs rename test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/{RelationshipsObject => }/CowStable.cs (96%) delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 79a25e7deb..f6d9dbdb2d 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -345,4 +345,3 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ } } } - diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 14cdfcc953..7c9cb75a15 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -238,4 +238,3 @@ private static bool IsDataPropertyNullableInRelationshipSchemaType(Type relation return NullableRelationshipSchemaOpenTypes.Contains(relationshipSchemaOpenType); } } - diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs new file mode 100644 index 0000000000..daf1bd38b6 --- /dev/null +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs @@ -0,0 +1,252 @@ +using System.Net; +using FluentAssertions; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; + +/// +/// Should consider if the shape of the two tests here is more favourable over the test with the same name in the RequestTests suite. The drawback of the +/// form here is that the expected json string is less easy to read. However the win is that this form allows us to run the tests in a [Theory]. This is +/// relevant because each of these properties represent unique test cases. In the other test form, it is not clear which properties are tested without. +/// For instance: here in Can_exclude_optional_relationships it is immediately clear that the properties we omit are those in the inline data. +/// +public sealed class AlternativeFormRequestTests +{ + private const string HenHouseUrl = "http://localhost/henHouses"; + + private readonly Dictionary _partials = new() + { + { + nameof(HenHouseRelationshipsInPostRequest.OldestChicken), @"""oldestChicken"": { + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"" + } + }" + }, + { + nameof(HenHouseRelationshipsInPostRequest.FirstChicken), @"""firstChicken"": { + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"" + } + }" + }, + { + nameof(HenHouseRelationshipsInPostRequest.AllChickens), @"""allChickens"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + }" + }, + + { + nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), @"""chickensReadyForLaying"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + }" + } + }; + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.OldestChicken))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens))] + public async Task Can_exclude_optional_relationships(string propertyName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHouseRelationshipsInPostRequest relationshipsObject = new() + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + relationshipsObject.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = relationshipsObject + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + string body = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""henHouses"", + ""relationships"": " + body + @" + } +}"); + } + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken))] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying))] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH(string propertyName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var relationshipsObject = new HenHouseRelationshipsInPatchRequest + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + relationshipsObject.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new HenHousePatchRequestDocument + { + Data = new HenHouseDataInPatchRequest + { + Id = "1", + Type = HenHouseResourceType.HenHouses, + Relationships = relationshipsObject + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + string serializedRelationshipsObject = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""henHouses"", + ""id"": ""1"", + ""relationships"": " + serializedRelationshipsObject + @" + } +}"); + } + + private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludeProperty) + { + string partial = ""; + + foreach ((string key, string relationshipJsonPartial) in _partials) + { + if (excludeProperty == key) + { + continue; + } + + if (partial.Length > 0) + { + partial += ",\n "; + } + + partial += relationshipJsonPartial; + } + + return @"{ + " + partial + @" + }"; + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs index 6670a0566f..c1a4eebab5 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs @@ -12,4 +12,3 @@ partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) settings.Formatting = Formatting.Indented; } } - diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs deleted file mode 100644 index 9ccda82859..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTests.cs +++ /dev/null @@ -1,559 +0,0 @@ -using System.Net; -using System.Reflection; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class RequestTests -{ - private const string HenHouseUrl = "http://localhost/henHouses"; - - [Fact] - public async Task Can_exclude_optional_relationships() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = new HenHouseRelationshipsInPostRequest - { - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": { - ""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePatchRequestDocument - { - Data = new HenHouseDataInPatchRequest - { - Id = "1", - Type = HenHouseResourceType.HenHouses, - Relationships = new HenHouseRelationshipsInPatchRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""id"": ""1"", - ""relationships"": { - ""oldestChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Fact] - public async Task Can_clear_nullable_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = new HenHouseRelationshipsInPostRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = null - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": { - ""oldestChicken"": { - ""data"": null - }, - ""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }, - ""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) - { - // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - } - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } -} - diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs deleted file mode 100644 index 870bb548fd..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/RequestTestsAlt.cs +++ /dev/null @@ -1,647 +0,0 @@ -using System.Net; -using System.Reflection; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class RequestTestsAlt -{ - private const string HenHouseUrl = "http://localhost/henHouses"; - - private readonly Dictionary _partials = new() - { - { - nameof(HenHouseRelationshipsInPostRequest.OldestChicken), @"""oldestChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }" - }, - { - nameof(HenHouseRelationshipsInPostRequest.FirstChicken), @"""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }" - }, - { - nameof(HenHouseRelationshipsInPostRequest.AllChickens), @"""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }" - }, - - { - nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), @"""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }" - } - }; - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.OldestChicken))] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens))] - public async Task Can_exclude_optional_relationships(string propertyName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsObject = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsObject.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsObject - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - string body = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": " + body + @" - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken))] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying))] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH(string propertyName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var relationshipsObject = new HenHouseRelationshipsInPatchRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsObject.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePatchRequestDocument - { - Data = new HenHouseDataInPatchRequest - { - Id = "1", - Type = HenHouseResourceType.HenHouses, - Relationships = relationshipsObject - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - string serializedRelationshipsObject = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""id"": ""1"", - ""relationships"": " + serializedRelationshipsObject + @" - } -}"); - } - - [Fact] - public async Task Can_clear_nullable_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = new HenHouseRelationshipsInPostRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = null - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": { - ""oldestChicken"": { - ""data"": null - }, - ""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }, - ""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } - - private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludeProperty) - { - string partial = ""; - - foreach ((string key, string relationshipJsonPartial) in _partials) - { - if (excludeProperty == key) - { - continue; - } - - if (partial.Length > 0) - { - partial += ",\n "; - } - - partial += relationshipJsonPartial; - } - - return @"{ - " + partial + @" - }"; - } -} - diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs index 8d62fa4fad..1ee3ee0fc3 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Reflection; using FluentAssertions; using FluentAssertions.Specialized; using JsonApiDotNetCore.Middleware; @@ -13,6 +14,7 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; public sealed class RelationshipRequestTests { private const string ChickenUrl = "http://localhost/chickens"; + private const string HenHouseUrl = "http://localhost/henHouses"; [Fact] public async Task Can_exclude_optional_attributes() @@ -327,5 +329,545 @@ public async Task Can_set_default_value_to_ValueType_attributes() } }"); } -} + [Fact] + public async Task Can_exclude_optional_relationships() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = new HenHouseRelationshipsInPostRequest + { + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""henHouses"", + ""relationships"": { + ""firstChicken"": { + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"" + } + }, + ""chickensReadyForLaying"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new HenHousePatchRequestDocument + { + Data = new HenHouseDataInPatchRequest + { + Id = "1", + Type = HenHouseResourceType.HenHouses, + Relationships = new HenHouseRelationshipsInPatchRequest + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""henHouses"", + ""id"": ""1"", + ""relationships"": { + ""oldestChicken"": { + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"" + } + }, + ""allChickens"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Fact] + public async Task Can_clear_nullable_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = new HenHouseRelationshipsInPostRequest + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = null + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(HenHouseUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""henHouses"", + ""relationships"": { + ""oldestChicken"": { + ""data"": null + }, + ""firstChicken"": { + ""data"": { + ""type"": ""chickens"", + ""id"": ""1"" + } + }, + ""allChickens"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + }, + ""chickensReadyForLaying"": { + ""data"": [ + { + ""type"": ""chickens"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] + public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + Func> action; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) + { + // Act + action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + } + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } + + [Theory] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] + [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] + public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestChicken = new NullableToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + FirstChicken = new ToOneChickenInRequest + { + Data = new ChickenIdentifier + { + Id = "1", + Type = ChickenResourceType.Chickens + } + }, + AllChickens = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + }, + ChickensReadyForLaying = new ToManyChickenInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = ChickenResourceType.Chickens + } + } + } + }; + + PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); + object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; + relationshipToClear.SetPropertyToDefaultValue("Data"); + + var requestDocument = new HenHousePostRequestDocument + { + Data = new HenHouseDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json index b694521108..8357d61d05 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json @@ -1228,7 +1228,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1297,7 +1297,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1334,7 +1334,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1357,7 +1357,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1404,7 +1404,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1433,7 +1433,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1459,7 +1459,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1528,7 +1528,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1569,7 +1569,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1664,7 +1664,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1821,7 +1821,7 @@ "type": "array" } ], - "items": {} + "items": { } }, "nullable": true }, @@ -1844,7 +1844,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1874,7 +1874,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1925,7 +1925,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1962,7 +1962,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1993,7 +1993,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs deleted file mode 100644 index 02659fc39e..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/RequestTests.cs +++ /dev/null @@ -1,487 +0,0 @@ -using System.Net; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; - -public sealed class RelationshipsRequestTests -{ - private const string CowStableUrl = "http://localhost/cowStables"; - - [Fact] - public async Task Can_exclude_optional_relationships() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = new CowStableRelationshipsInPostRequest - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowStableUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""relationships"": { - ""oldestCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""firstCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""favoriteCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""allCows"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Theory] - [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - AlbinoCow = new NullableToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePatchRequestDocument - { - Data = new CowStableDataInPatchRequest - { - Id = "1", - Type = CowStableResourceType.CowStables, - Relationships = new CowStableRelationshipsInPatchRequest - { - AlbinoCow = new NullableToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(CowStableUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""id"": ""1"", - ""relationships"": { - ""albinoCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""cowsReadyForMilking"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Fact] - public async Task Can_clear_nullable_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = new CowStableRelationshipsInPostRequest - { - AlbinoCow = new NullableToOneCowInRequest - { - Data = null - }, - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowStableUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""relationships"": { - ""oldestCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""firstCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""albinoCow"": { - ""data"": null - }, - ""favoriteCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""cowsReadyForMilking"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - }, - ""allCows"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } -} - diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs index 50fdf9c355..aef305b63d 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs @@ -13,6 +13,7 @@ namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; public sealed class RequestTests { private const string CowUrl = "http://localhost/cows"; + private const string CowStableUrl = "http://localhost/cowStables"; [Fact] public async Task Can_exclude_optional_attributes() @@ -304,6 +305,476 @@ public async Task Can_set_default_value_to_ValueType_attributes() ""hasProducedMilk"": false } } +}"); + } + + [Fact] + public async Task Can_exclude_optional_relationships() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowStablePostRequestDocument + { + Data = new CowStableDataInPostRequest + { + Relationships = new CowStableRelationshipsInPostRequest + { + OldestCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FirstCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FavoriteCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + AllCows = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(CowStableUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cowStables"", + ""relationships"": { + ""oldestCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""firstCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""favoriteCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""allCows"": { + ""data"": [ + { + ""type"": ""cows"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Theory] + [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FirstCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FavoriteCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + CowsReadyForMilking = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + }, + AllCows = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new CowStablePostRequestDocument + { + Data = new CowStableDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Theory] + [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] + [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] + public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() + { + OldestCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FirstCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + AlbinoCow = new NullableToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FavoriteCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + CowsReadyForMilking = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + }, + AllCows = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + } + }; + + relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); + + var requestDocument = new CowStablePostRequestDocument + { + Data = new CowStableDataInPostRequest + { + Relationships = relationshipsInPostDocument + } + }; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowStablePatchRequestDocument + { + Data = new CowStableDataInPatchRequest + { + Id = "1", + Type = CowStableResourceType.CowStables, + Relationships = new CowStableRelationshipsInPatchRequest + { + AlbinoCow = new NullableToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + CowsReadyForMilking = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(1, requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be(CowStableUrl + "/1"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cowStables"", + ""id"": ""1"", + ""relationships"": { + ""albinoCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""cowsReadyForMilking"": { + ""data"": [ + { + ""type"": ""cows"", + ""id"": ""1"" + } + ] + } + } + } +}"); + } + + [Fact] + public async Task Can_clear_nullable_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + var requestDocument = new CowStablePostRequestDocument + { + Data = new CowStableDataInPostRequest + { + Relationships = new CowStableRelationshipsInPostRequest + { + AlbinoCow = new NullableToOneCowInRequest + { + Data = null + }, + OldestCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FirstCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + FavoriteCow = new ToOneCowInRequest + { + Data = new CowIdentifier + { + Id = "1", + Type = CowResourceType.Cows + } + }, + CowsReadyForMilking = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + }, + AllCows = new ToManyCowInRequest + { + Data = new List + { + new() + { + Id = "1", + Type = CowResourceType.Cows + } + } + } + } + } + }; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be(CowStableUrl); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + wrapper.RequestBody.Should().BeJson(@"{ + ""data"": { + ""type"": ""cowStables"", + ""relationships"": { + ""oldestCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""firstCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""albinoCow"": { + ""data"": null + }, + ""favoriteCow"": { + ""data"": { + ""type"": ""cows"", + ""id"": ""1"" + } + }, + ""cowsReadyForMilking"": { + ""data"": [ + { + ""type"": ""cows"", + ""id"": ""1"" + } + ] + }, + ""allCows"": { + ""data"": [ + { + ""type"": ""cows"", + ""id"": ""1"" + } + ] + } + } + } }"); } } diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json index d32dc3f54c..1f2d189d46 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json +++ b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json @@ -1553,7 +1553,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1622,7 +1622,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1659,7 +1659,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1682,7 +1682,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1729,7 +1729,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1758,7 +1758,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1784,7 +1784,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -1853,7 +1853,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -1894,7 +1894,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -2009,7 +2009,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -2166,7 +2166,7 @@ "type": "array" } ], - "items": {} + "items": { } }, "nullable": true }, @@ -2189,7 +2189,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -2219,7 +2219,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } }, "jsonapi": { "$ref": "#/components/schemas/jsonapiObject" @@ -2270,7 +2270,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -2307,7 +2307,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false @@ -2338,7 +2338,7 @@ }, "meta": { "type": "object", - "additionalProperties": {} + "additionalProperties": { } } }, "additionalProperties": false diff --git a/test/OpenApiTests/JsonElementExtensions.cs b/test/OpenApiTests/JsonElementExtensions.cs index 93d903baac..3649127917 100644 --- a/test/OpenApiTests/JsonElementExtensions.cs +++ b/test/OpenApiTests/JsonElementExtensions.cs @@ -41,7 +41,7 @@ public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElem return new SchemaReferenceIdContainer(value); } - public static string GetSchemaReferenceId(this JsonElement source) + private static string GetSchemaReferenceId(this JsonElement source) { source.ValueKind.Should().Be(JsonValueKind.String); @@ -98,4 +98,3 @@ public void ContainProperty(string propertyName) } } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs similarity index 96% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs rename to test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs index b148444378..979b029717 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/HenHouse.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs @@ -5,7 +5,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs index 0149a07e32..0b88c3f88b 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs @@ -17,6 +17,7 @@ public ModelStateValidationDisabledTests( _testContext = testContext; testContext.UseController(); + testContext.UseController(); } [Theory] @@ -64,4 +65,48 @@ public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required // Assert document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); } + + [Theory] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("oldestChicken")] + [InlineData("allChickens")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs index e200fe365c..bd31a1f25a 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs @@ -16,6 +16,7 @@ public ModelStateValidationEnabledTests( _testContext = testContext; testContext.UseController(); + testContext.UseController(); } [Theory] @@ -63,4 +64,48 @@ public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required // Assert document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); } + + [Theory] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("oldestChicken")] + [InlineData("allChickens")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs index 7d79bf9e6d..dca51da02b 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -22,7 +22,7 @@ public NullabilityTests(OpenApiTestContext + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("allChickens")] + [InlineData("firstChicken")] + [InlineData("chickensReadyForLaying")] + public async Task Data_property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs index 57c3d96f17..a93f4bf779 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -1,6 +1,5 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs deleted file mode 100644 index b9052eda76..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestChicken")] - [InlineData("allChickens")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs deleted file mode 100644 index 9bea8d4062..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestChicken")] - [InlineData("allChickens")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs deleted file mode 100644 index 544c1cd9ec..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/RelationshipsObject/NullabilityTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Text.Json; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled.RelationshipsObject; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("oldestChicken")] - public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("allChickens")] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs index f876351bec..3b5562a0b3 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs @@ -37,5 +37,3 @@ public sealed class Cow : Identifiable [Required] public bool? HasProducedMilk { get; set; } } - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs similarity index 96% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs rename to test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs index c2f4ad9367..b267423501 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/CowStable.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; +namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs index 988fe06896..fb83c4ea5f 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs @@ -17,6 +17,7 @@ public ModelStateValidationDisabledTests( _testContext = testContext; testContext.UseController(); + testContext.UseController(); } [Theory] @@ -65,4 +66,50 @@ public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required // Assert document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); } + + [Theory] + [InlineData("firstCow")] + [InlineData("allCows")] + [InlineData("favoriteCow")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("oldestCow")] + [InlineData("cowsReadyForMilking")] + [InlineData("albinoCow")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs index 70e4b3f7a4..d45937a580 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs @@ -16,6 +16,7 @@ public ModelStateValidationEnabledTests( _testContext = testContext; testContext.UseController(); + testContext.UseController(); } [Theory] @@ -64,4 +65,50 @@ public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required // Assert document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); } + + [Theory] + [InlineData("oldestCow")] + [InlineData("firstCow")] + [InlineData("allCows")] + [InlineData("favoriteCow")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain(propertyName); + }); + } + + [Theory] + [InlineData("cowsReadyForMilking")] + [InlineData("albinoCow")] + public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain(propertyName); + }); + } + + [Fact] + public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); + } } diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs index 99100b8353..5652c61f33 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -58,6 +58,42 @@ public async Task Property_in_schema_for_attribute_of_resource_should_not_be_nul }); }); } -} + [Theory] + [InlineData("albinoCow")] + public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Theory] + [InlineData("oldestCow")] + [InlineData("firstCow")] + [InlineData("cowsReadyForMilking")] + [InlineData("allCows")] + [InlineData("favoriteCow")] + public async Task Data_property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs index 278349c29a..e83298f28d 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -1,6 +1,5 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; using TestBuildingBlocks; namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; @@ -41,4 +40,3 @@ protected override void OnModelCreating(ModelBuilder builder) base.OnModelCreating(builder); } } - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs deleted file mode 100644 index f52097c764..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("firstCow")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("cowsReadyForMilking")] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs deleted file mode 100644 index 946e41ea27..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("firstCow")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("cowsReadyForMilking")] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} - - diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs deleted file mode 100644 index ed599af06c..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/RelationshipsObject/NullabilityTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Text.Json; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled.RelationshipsObject; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - } - - [Theory] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("firstCow")] - [InlineData("cowsReadyForMilking")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} - - From 76e098aab019ec38bf82c6186fa33806138022fb Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 28 Dec 2022 23:08:51 +0100 Subject: [PATCH 20/66] Organise tests such that they map directly to the tables in #1231 and #1111 Organise tests such that they map directly to the tables in #1231 and #1111 --- appveyor.yml | 1 - .../FakeHttpClientWrapper.cs | 12 + test/OpenApiClientTests/ObjectExtensions.cs | 20 - .../OpenApiClientTests.csproj | 8 +- .../NullableReferenceTypesDisabledClient.cs | 2 +- .../NullabilityTests.cs | 4 +- .../NullableReferenceTypesDisabledFaker.cs | 124 +++ .../RequestTests.cs | 636 +++++++++++++ .../swagger.g.json | 0 .../NullableReferenceTypesEnabledClient.cs | 2 +- .../NullabilityTests.cs | 4 +- .../NullableReferenceTypesEnabledFaker.cs | 132 +++ .../RequestTests.cs | 724 +++++++++++++++ .../swagger.g.json | 0 .../AlternativeFormRequestTests.cs | 252 ----- .../RequestTests.cs | 873 ------------------ .../RequestTests.cs | 780 ---------------- test/OpenApiTests/OpenApiTests.csproj | 3 +- .../ModelStateValidationDisabledStartup.cs | 2 +- .../NullableReferenceTypesDisabled/Chicken.cs | 4 +- .../HenHouse.cs | 4 +- .../NullabilityTests.cs | 181 ++++ ...NullableReferenceTypesDisabledDbContext.cs | 2 +- .../ModelStateValidationDisabledTests.cs | 192 ++++ .../ModelStateValidationEnabledTests.cs | 191 ++++ .../NullableReferenceTypesEnabled/Cow.cs | 4 +- .../CowStable.cs | 4 +- .../NullabilityTests.cs | 245 +++++ .../NullableReferenceTypesEnabledDbContext.cs | 2 +- .../ModelStateValidationDisabledTests.cs | 252 +++++ .../ModelStateValidationEnabledTests.cs | 251 +++++ .../ModelStateValidationDisabledTests.cs | 112 --- .../ModelStateValidationEnabledTests.cs | 111 --- .../NullabilityTests.cs | 95 -- .../ModelStateValidationDisabledTests.cs | 115 --- .../ModelStateValidationEnabledTests.cs | 114 --- .../NullabilityTests.cs | 99 -- .../JsonElementExtensions.cs | 15 +- .../TestBuildingBlocks.csproj | 1 + 39 files changed, 2973 insertions(+), 2600 deletions(-) delete mode 100644 test/OpenApiClientTests/ObjectExtensions.cs rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs (77%) rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/NullabilityTests.cs (85%) create mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs create mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/swagger.g.json (100%) rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs (77%) rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/NullabilityTests.cs (86%) create mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs create mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs rename test/OpenApiClientTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/swagger.g.json (100%) delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs delete mode 100644 test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/ModelStateValidationDisabledStartup.cs (90%) rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/Chicken.cs (80%) rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/HenHouse.cs (80%) create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs (92%) create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/Cow.cs (83%) rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/CowStable.cs (82%) create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs rename test/OpenApiTests/{SchemaProperties => ResourceFieldsValidation}/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs (93%) create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs create mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs rename test/{OpenApiTests => TestBuildingBlocks}/JsonElementExtensions.cs (87%) diff --git a/appveyor.yml b/appveyor.yml index bbd35ed160..433d391b16 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,6 @@ environment: branches: only: - - openapi-required-and-nullable-properties # TODO: remove - master - openapi - develop diff --git a/test/OpenApiClientTests/FakeHttpClientWrapper.cs b/test/OpenApiClientTests/FakeHttpClientWrapper.cs index 222650ac2d..8226a4e496 100644 --- a/test/OpenApiClientTests/FakeHttpClientWrapper.cs +++ b/test/OpenApiClientTests/FakeHttpClientWrapper.cs @@ -1,6 +1,7 @@ using System.Net; using System.Net.Http.Headers; using System.Text; +using System.Text.Json; using JsonApiDotNetCore.OpenApi.Client; namespace OpenApiClientTests; @@ -22,6 +23,17 @@ private FakeHttpClientWrapper(HttpClient httpClient, FakeHttpMessageHandler hand _handler = handler; } + public JsonElement ParseRequestBody() + { + if (RequestBody == null) + { + throw new InvalidOperationException(); + } + + using JsonDocument jsonDocument = JsonDocument.Parse(RequestBody); + return jsonDocument.RootElement.Clone(); + } + public static FakeHttpClientWrapper Create(HttpStatusCode statusCode, string? responseBody) { HttpResponseMessage response = CreateResponse(statusCode, responseBody); diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs deleted file mode 100644 index e3790eaa15..0000000000 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Reflection; -using JsonApiDotNetCore.OpenApi.Client; - -namespace OpenApiClientTests; - -internal static class ObjectExtensions -{ - public static void SetPropertyToDefaultValue(this object target, string propertyName) - { - ArgumentGuard.NotNull(target); - ArgumentGuard.NotNull(propertyName); - - Type declaringType = target.GetType(); - - PropertyInfo property = declaringType.GetProperties().Single(property => property.Name == propertyName); - object? defaultValue = declaringType.IsValueType ? Activator.CreateInstance(declaringType) : null; - - property.SetValue(target, defaultValue); - } -} diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index badeeaeae7..4e726ad900 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -52,15 +52,15 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions - - OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode + + OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode NullableReferenceTypesEnabledClient NullableReferenceTypesEnabledClient.cs NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - - OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode + + OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode NullableReferenceTypesDisabledClient NullableReferenceTypesDisabledClient.cs NSwagCSharp diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs similarity index 77% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs index c1a4eebab5..8c6b98a237 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; internal partial class NullableReferenceTypesDisabledClient : JsonApiClient { diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs similarity index 85% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs index 76c80cbf73..a979e26901 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; using Xunit; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs new file mode 100644 index 0000000000..1f63b62ff9 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs @@ -0,0 +1,124 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; + +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; + +// @formatter:wrap_chained_method_calls chop_always +// @formatter:keep_existing_linebreaks true + +internal sealed class NullableReferenceTypesDisabledFaker +{ + private readonly Lazy> _lazyHenHousePostRequestDocumentFaker; + private readonly Lazy> _lazyHenHousePatchRequestDocumentFaker; + + private readonly Lazy> _lazyChickenPostRequestDocumentFaker = new(() => + { + Faker attributesInPostRequestFaker = new Faker() + .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) + .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) + .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) + .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) + .RuleFor(attributes => attributes.HasProducedEggs, _ => true); + + Faker dataInPostRequestFaker = new Faker() + .RuleFor(data => data.Attributes, _ => attributesInPostRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); + }); + + private readonly Lazy> _lazyChickenPatchRequestDocumentFaker = new(() => + { + Faker attributesInPatchRequestFaker = new Faker() + .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) + .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) + .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) + .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) + .RuleFor(attributes => attributes.HasProducedEggs, _ => true); + + Faker dataInPatchRequestFaker = new Faker() + // @formatter:wrap_chained_method_calls chop_if_long + .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) + // @formatter:wrap_chained_method_calls restore + .RuleFor(data => data.Attributes, _ => attributesInPatchRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); + }); + + private readonly Lazy> _lazyToOneChickenInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new ChickenIdentifier + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + })); + + private readonly Lazy> _lazyNullableToOneChickenInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new ChickenIdentifier + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + })); + + private readonly Lazy> _lazyToManyChickenInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new List + { + new() + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + } + })); + + public Faker ChickenPostRequestDocument => _lazyChickenPostRequestDocumentFaker.Value; + public Faker ChickenPatchRequestDocument => _lazyChickenPatchRequestDocumentFaker.Value; + public Faker HenHousePostRequestDocument => _lazyHenHousePostRequestDocumentFaker.Value; + public Faker HenHousePatchRequestDocument => _lazyHenHousePatchRequestDocumentFaker.Value; + + public NullableReferenceTypesDisabledFaker() + { + _lazyHenHousePostRequestDocumentFaker = new Lazy>(HenHousePostRequestDocumentFakerFactory); + _lazyHenHousePatchRequestDocumentFaker = new Lazy>(HenHousePatchRequestDocumentFakerFactory); + } + + private Faker HenHousePostRequestDocumentFakerFactory() + { + Faker relationshipsInPostRequestFaker = new Faker() + .RuleFor(relationships => relationships.OldestChicken, _ => _lazyNullableToOneChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FirstChicken, _ => _lazyToOneChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.ChickensReadyForLaying, _ => _lazyToManyChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AllChickens, _ => _lazyToManyChickenInRequestFaker.Value.Generate()); + + Faker dataInPostRequestFaker = new Faker() + .RuleFor(data => data.Relationships, _ => relationshipsInPostRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); + } + + private Faker HenHousePatchRequestDocumentFakerFactory() + { + Faker relationshipsInPatchRequestFaker = new Faker() + .RuleFor(relationships => relationships.OldestChicken, _ => _lazyNullableToOneChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FirstChicken, _ => _lazyToOneChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.ChickensReadyForLaying, _ => _lazyToManyChickenInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AllChickens, _ => _lazyToManyChickenInRequestFaker.Value.Generate()); + + Faker dataInPatchRequestFaker = new Faker() + // @formatter:wrap_chained_method_calls chop_if_long + .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) + // @formatter:wrap_chained_method_calls restore + .RuleFor(data => data.Relationships, _ => relationshipsInPatchRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs new file mode 100644 index 0000000000..35499a2cc0 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs @@ -0,0 +1,636 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; + +public sealed class RequestTests +{ + private readonly NullableReferenceTypesDisabledFaker _fakers = new(); + + [Fact] + public async Task Can_clear_reference_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Name = null; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + chicken => chicken.Name)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("name").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Fact] + public async Task Can_exclude_reference_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Name = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("name"); + }); + } + + [Fact] + public async Task Cannot_clear_required_reference_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.NameOfCurrentFarm = default; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); + } + + [Fact] + public async Task Cannot_exclude_required_reference_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.NameOfCurrentFarm = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'nameOfCurrentFarm' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_set_default_value_to_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Age = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("age").With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Fact] + public async Task Can_exclude_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Age = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("age"); + }); + } + + [Fact] + public async Task Can_set_default_value_to_required_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Weight = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("weight").With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Fact] + public async Task Cannot_exclude_required_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Weight = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'weight' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_clear_nullable_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = null; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + chicken => chicken.TimeAtCurrentFarmInDays)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("timeAtCurrentFarmInDays").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Fact] + public async Task Can_exclude_nullable_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_nullable_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.HasProducedEggs = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'hasProducedEggs' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_set_default_value_to_required_nullable_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); + requestDocument.Data.Attributes.HasProducedEggs = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("hasProducedEggs").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.False)); + }); + } + + [Fact] + public async Task Can_clear_has_one_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestChicken.Data = null; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships.oldestChicken.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Fact] + public async Task Can_exclude_has_one_relationship_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestChicken = default!; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("oldestChicken"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_has_one_relationship_without_document_registration_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FirstChicken = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'firstChicken'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Cannot_exclude_required_has_one_relationship_with_document_registration_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FirstChicken = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'firstChicken' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Can_exclude_has_many_relationship_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.AllChickens = default!; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("allChickens"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_has_many_relationship_with_document_registration_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.ChickensReadyForLaying = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'chickensReadyForLaying' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Cannot_exclude_required_has_many_relationship_without_document_registration_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); + requestDocument.Data.Relationships.ChickensReadyForLaying = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'chickensReadyForLaying'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Cannot_exclude_id_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPatchRequestDocument requestDocument = _fakers.ChickenPatchRequestDocument.Generate(); + requestDocument.Data.Id = default!; + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Fact] + public async Task Attributes_required_in_POST_request_are_not_required_in_PATCH_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + ChickenPatchRequestDocument requestDocument = _fakers.ChickenPatchRequestDocument.Generate(); + requestDocument.Data.Attributes.NameOfCurrentFarm = default!; + requestDocument.Data.Attributes.Weight = default!; + requestDocument.Data.Attributes.HasProducedEggs = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be($"http://localhost/chickens/{int.Parse(requestDocument.Data.Id)}"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("nameOfCurrentFarm"); + attributesObject.ShouldNotContainPath("weight"); + attributesObject.ShouldNotContainPath("hasProducedMilk"); + }); + } + + [Fact] + public async Task Relationships_required_in_POST_request_are_not_required_in_PATCH_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); + + HenHousePatchRequestDocument requestDocument = _fakers.HenHousePatchRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestChicken = default!; + requestDocument.Data.Relationships.FirstChicken = default!; + requestDocument.Data.Relationships.ChickensReadyForLaying = default!; + requestDocument.Data.Relationships.AllChickens = default!; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be($"http://localhost/henHouses/{int.Parse(requestDocument.Data.Id)}"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("oldestChicken"); + relationshipsObject.ShouldNotContainPath("firstChicken"); + relationshipsObject.ShouldNotContainPath("favoriteChicken"); + relationshipsObject.ShouldNotContainPath("allChickens"); + }); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/swagger.g.json diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs similarity index 77% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs index 17b822f56d..aa15aa3c91 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; internal partial class NullableReferenceTypesEnabledClient : JsonApiClient { diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs similarity index 86% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs index d351867b8b..e3d01f54d4 100644 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; using Xunit; -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs new file mode 100644 index 0000000000..c999cecef2 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs @@ -0,0 +1,132 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; + +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; + +// @formatter:wrap_chained_method_calls chop_always +// @formatter:keep_existing_linebreaks true + +internal sealed class NullableReferenceTypesEnabledFaker +{ + private readonly Lazy> _lazyCowStablePostRequestDocumentFaker; + private readonly Lazy> _lazyCowStablePatchRequestDocumentFaker; + + private readonly Lazy> _lazyCowPostRequestDocumentFaker = new(() => + { + Faker attributesInPostRequestFaker = new Faker() + .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) + .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.NameOfPreviousFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.Nickname, faker => faker.Internet.UserName()) + .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) + .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) + .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) + .RuleFor(attributes => attributes.HasProducedMilk, _ => true); + + Faker dataInPostRequestFaker = new Faker() + .RuleFor(data => data.Attributes, _ => attributesInPostRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); + }); + + private readonly Lazy> _lazyCowPatchRequestDocumentFaker = new(() => + { + Faker attributesInPatchRequestFaker = new Faker() + .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) + .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.NameOfPreviousFarm, faker => faker.Company.CompanyName()) + .RuleFor(attributes => attributes.Nickname, faker => faker.Internet.UserName()) + .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) + .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) + .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) + .RuleFor(attributes => attributes.HasProducedMilk, _ => true); + + Faker dataInPatchRequestFaker = new Faker() + // @formatter:wrap_chained_method_calls chop_if_long + .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) + // @formatter:wrap_chained_method_calls restore + .RuleFor(data => data.Attributes, _ => attributesInPatchRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); + }); + + private readonly Lazy> _lazyToOneCowInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new CowIdentifier + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + })); + + private readonly Lazy> _lazyNullableToOneCowInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new CowIdentifier + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + })); + + private readonly Lazy> _lazyToManyCowInRequestFaker = new(() => + new Faker() + .RuleFor(relationship => relationship.Data, faker => new List + { + new() + { + // @formatter:wrap_chained_method_calls chop_if_long + Id = faker.Random.Int(1, 100).ToString() + // @formatter:wrap_chained_method_calls restore + } + })); + + public Faker CowPostRequestDocument => _lazyCowPostRequestDocumentFaker.Value; + public Faker CowPatchRequestDocument => _lazyCowPatchRequestDocumentFaker.Value; + public Faker CowStablePostRequestDocument => _lazyCowStablePostRequestDocumentFaker.Value; + public Faker CowStablePatchRequestDocument => _lazyCowStablePatchRequestDocumentFaker.Value; + + public NullableReferenceTypesEnabledFaker() + { + _lazyCowStablePostRequestDocumentFaker = new Lazy>(CreateCowStablePostRequestDocumentFaker); + _lazyCowStablePatchRequestDocumentFaker = new Lazy>(CreateCowStablePatchRequestDocumentFaker); + } + + private Faker CreateCowStablePostRequestDocumentFaker() + { + Faker relationshipsInPostRequestFaker = new Faker() + .RuleFor(relationships => relationships.OldestCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FirstCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AlbinoCow, _ => _lazyNullableToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FavoriteCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.CowsReadyForMilking, _ => _lazyToManyCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AllCows, _ => _lazyToManyCowInRequestFaker.Value.Generate()); + + Faker dataInPostRequestFaker = new Faker() + .RuleFor(data => data.Relationships, _ => relationshipsInPostRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); + } + + private Faker CreateCowStablePatchRequestDocumentFaker() + { + Faker relationshipsInPatchRequestFaker = new Faker() + .RuleFor(relationships => relationships.OldestCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FirstCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AlbinoCow, _ => _lazyNullableToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.FavoriteCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.CowsReadyForMilking, _ => _lazyToManyCowInRequestFaker.Value.Generate()) + .RuleFor(relationships => relationships.AllCows, _ => _lazyToManyCowInRequestFaker.Value.Generate()); + + Faker dataInPatchRequestFaker = new Faker() + // @formatter:wrap_chained_method_calls chop_if_long + .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) + // @formatter:wrap_chained_method_calls restore + .RuleFor(data => data.Relationships, _ => relationshipsInPatchRequestFaker.Generate()); + + return new Faker() + .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs new file mode 100644 index 0000000000..2a509fef2a --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs @@ -0,0 +1,724 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.Middleware; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; + +public sealed class RequestTests +{ + private readonly NullableReferenceTypesEnabledFaker _fakers = new(); + + [Fact] + public async Task Cannot_exclude_non_nullable_reference_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Name = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'name' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Cannot_exclude_required_non_nullable_reference_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.NameOfCurrentFarm = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'nameOfCurrentFarm' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_exclude_nullable_reference_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.NameOfPreviousFarm = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("nameOfPreviousFarm"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_nullable_reference_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Nickname = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'nickname' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_set_default_value_to_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Age = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("age").With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Fact] + public async Task Can_exclude_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Age = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("age"); + }); + } + + [Fact] + public async Task Can_set_default_value_to_required_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Weight = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("weight").With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Fact] + public async Task Cannot_exclude_required_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.Weight = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'weight' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Can_clear_nullable_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = null; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + cow => cow.TimeAtCurrentFarmInDays)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("timeAtCurrentFarmInDays").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Fact] + public async Task Can_exclude_nullable_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Can_set_default_value_to_required_nullable_value_type_attribute() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.HasProducedMilk = default; + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath("hasProducedMilk").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.False)); + }); + } + + [Fact] + public async Task Cannot_exclude_required_nullable_value_type_attribute_in_POST_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); + requestDocument.Data.Attributes.HasProducedMilk = default; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'hasProducedMilk' must have a value because it is required. Path 'data.attributes'."); + } + } + + [Fact] + public async Task Cannot_exclude_has_one_relationship_in_POST_request_with_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestCow = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'oldestCow' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Cannot_exclude_has_one_relationship_in_POST_request_without_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestCow = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'oldestCow'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Cannot_exclude_required_has_one_relationship_in_POST_request_with_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FirstCow = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'firstCow' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Cannot_exclude_required_has_one_relationship_in_POST_request_without_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FirstCow = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'firstCow'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_clear_nullable_has_one_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.AlbinoCow.Data = null; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships.albinoCow.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Fact] + public async Task Can_exclude_nullable_has_one_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.AlbinoCow = default!; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("albinoCow"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_nullable_has_one_relationship_in_POST_request_with_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FavoriteCow = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'favoriteCow' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Cannot_exclude_required_nullable_has_one_relationship_in_POST_request_without_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.FavoriteCow = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'favoriteCow'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Can_exclude_has_many_relationship() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.CowsReadyForMilking = default!; + + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Post); + wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("cowsReadyForMilking"); + }); + } + + [Fact] + public async Task Cannot_exclude_required_has_many_relationship_in_POST_request_with_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.AllCows = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Ignored property 'allCows' must have a value because it is required. Path 'data.relationships'."); + } + } + + [Fact] + public async Task Cannot_exclude_required_has_many_relationship_in_POST_request_without_document_registration() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); + requestDocument.Data.Relationships.AllCows = default!; + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'allCows'. Property requires a value. Path 'data.relationships'."); + } + + [Fact] + public async Task Cannot_exclude_id_when_performing_PATCH() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPatchRequestDocument requestDocument = _fakers.CowPatchRequestDocument.Generate(); + requestDocument.Data.Id = default!; + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Fact] + public async Task Attributes_required_in_POST_request_are_not_required_in_PATCH_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowPatchRequestDocument requestDocument = _fakers.CowPatchRequestDocument.Generate(); + requestDocument.Data.Attributes.Name = default!; + requestDocument.Data.Attributes.NameOfCurrentFarm = default!; + requestDocument.Data.Attributes.Nickname = default!; + requestDocument.Data.Attributes.Weight = default!; + requestDocument.Data.Attributes.HasProducedMilk = default!; + + using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be($"http://localhost/cows/{int.Parse(requestDocument.Data.Id)}"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath("name"); + attributesObject.ShouldNotContainPath("nameOfCurrentFarm"); + attributesObject.ShouldNotContainPath("nickname"); + attributesObject.ShouldNotContainPath("weight"); + attributesObject.ShouldNotContainPath("hasProducedMilk"); + }); + } + + [Fact] + public async Task Relationships_required_in_POST_request_are_not_required_in_PATCH_request() + { + // Arrange + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); + + CowStablePatchRequestDocument requestDocument = _fakers.CowStablePatchRequestDocument.Generate(); + requestDocument.Data.Relationships.OldestCow = default!; + requestDocument.Data.Relationships.FirstCow = default!; + requestDocument.Data.Relationships.FavoriteCow = default!; + requestDocument.Data.Relationships.AllCows = default!; + + await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + + // Assert + wrapper.Request.ShouldNotBeNull(); + wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); + wrapper.Request.Method.Should().Be(HttpMethod.Patch); + wrapper.Request.RequestUri.Should().Be($"http://localhost/cowStables/{int.Parse(requestDocument.Data.Id)}"); + wrapper.Request.Content.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); + wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); + + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath("oldestCow"); + relationshipsObject.ShouldNotContainPath("firstCow"); + relationshipsObject.ShouldNotContainPath("favoriteCow"); + relationshipsObject.ShouldNotContainPath("allCows"); + }); + } +} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/swagger.g.json diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs deleted file mode 100644 index daf1bd38b6..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/AlternativeFormRequestTests.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System.Net; -using FluentAssertions; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; - -/// -/// Should consider if the shape of the two tests here is more favourable over the test with the same name in the RequestTests suite. The drawback of the -/// form here is that the expected json string is less easy to read. However the win is that this form allows us to run the tests in a [Theory]. This is -/// relevant because each of these properties represent unique test cases. In the other test form, it is not clear which properties are tested without. -/// For instance: here in Can_exclude_optional_relationships it is immediately clear that the properties we omit are those in the inline data. -/// -public sealed class AlternativeFormRequestTests -{ - private const string HenHouseUrl = "http://localhost/henHouses"; - - private readonly Dictionary _partials = new() - { - { - nameof(HenHouseRelationshipsInPostRequest.OldestChicken), @"""oldestChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }" - }, - { - nameof(HenHouseRelationshipsInPostRequest.FirstChicken), @"""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }" - }, - { - nameof(HenHouseRelationshipsInPostRequest.AllChickens), @"""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }" - }, - - { - nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), @"""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }" - } - }; - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.OldestChicken))] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens))] - public async Task Can_exclude_optional_relationships(string propertyName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsObject = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsObject.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsObject - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - string body = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": " + body + @" - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken))] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying))] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH(string propertyName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var relationshipsObject = new HenHouseRelationshipsInPatchRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsObject.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePatchRequestDocument - { - Data = new HenHouseDataInPatchRequest - { - Id = "1", - Type = HenHouseResourceType.HenHouses, - Relationships = relationshipsObject - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - string serializedRelationshipsObject = GetRelationshipsObjectWithSinglePropertyOmitted(propertyName); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""id"": ""1"", - ""relationships"": " + serializedRelationshipsObject + @" - } -}"); - } - - private string GetRelationshipsObjectWithSinglePropertyOmitted(string excludeProperty) - { - string partial = ""; - - foreach ((string key, string relationshipJsonPartial) in _partials) - { - if (excludeProperty == key) - { - continue; - } - - if (partial.Length > 0) - { - partial += ",\n "; - } - - partial += relationshipJsonPartial; - } - - return @"{ - " + partial + @" - }"; - } -} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs deleted file mode 100644 index 1ee3ee0fc3..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled/RequestTests.cs +++ /dev/null @@ -1,873 +0,0 @@ -using System.Net; -using System.Reflection; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class RelationshipRequestTests -{ - private const string ChickenUrl = "http://localhost/chickens"; - private const string HenHouseUrl = "http://localhost/henHouses"; - - [Fact] - public async Task Can_exclude_optional_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPostRequestDocument - { - Data = new ChickenDataInPostRequest - { - Attributes = new ChickenAttributesInPostRequest - { - NameOfCurrentFarm = "Cow and Chicken Farm", - Weight = 30, - HasProducedEggs = true - } - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(ChickenUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""chickens"", - ""attributes"": { - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""weight"": 30, - ""hasProducedEggs"": true - } - } -}"); - } - - [Theory] - [InlineData(nameof(ChickenAttributesInResponse.NameOfCurrentFarm), "nameOfCurrentFarm")] - [InlineData(nameof(ChickenAttributesInResponse.Weight), "weight")] - [InlineData(nameof(ChickenAttributesInResponse.HasProducedEggs), "hasProducedEggs")] - public async Task Cannot_exclude_required_attribute_when_performing_POST(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var attributesInPostRequest = new ChickenAttributesInPostRequest - { - Name = "Chicken", - NameOfCurrentFarm = "Cow and Chicken Farm", - Age = 10, - Weight = 30, - TimeAtCurrentFarmInDays = 100, - HasProducedEggs = true - }; - - attributesInPostRequest.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new ChickenPostRequestDocument - { - Data = new ChickenDataInPostRequest - { - Attributes = attributesInPostRequest - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_exclude_attributes_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPatchRequestDocument - { - Data = new ChickenDataInPatchRequest - { - Id = "1", - Attributes = new ChickenAttributesInPatchRequest - { - Name = "Chicken", - Age = 10, - TimeAtCurrentFarmInDays = 100 - } - } - }; - - // Act - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(1, requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(ChickenUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"", - ""attributes"": { - ""name"": ""Chicken"", - ""age"": 10, - ""timeAtCurrentFarmInDays"": 100 - } - } -}"); - } - - [Fact] - public async Task Cannot_exclude_id_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPatchRequestDocument - { - Data = new ChickenDataInPatchRequest - { - Attributes = new ChickenAttributesInPatchRequest - { - Name = "Chicken", - NameOfCurrentFarm = "Cow and Chicken Farm", - Age = 10, - Weight = 30, - TimeAtCurrentFarmInDays = 100, - HasProducedEggs = true - } - } - }; - - // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(1, requestDocument)); - - // Assert - await action.Should().ThrowAsync(); - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); - } - - [Fact] - public async Task Can_clear_nullable_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPostRequestDocument - { - Data = new ChickenDataInPostRequest - { - Attributes = new ChickenAttributesInPostRequest - { - Name = null, - TimeAtCurrentFarmInDays = null, - NameOfCurrentFarm = "Cow and Chicken Farm", - Age = 10, - Weight = 30, - HasProducedEggs = true - } - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - chicken => chicken.Name, chicken => chicken.TimeAtCurrentFarmInDays)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(ChickenUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""chickens"", - ""attributes"": { - ""name"": null, - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""age"": 10, - ""weight"": 30, - ""timeAtCurrentFarmInDays"": null, - ""hasProducedEggs"": true - } - } -}"); - } - - [Fact] - public async Task Cannot_clear_required_attribute_when_performing_POST() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPostRequestDocument - { - Data = new ChickenDataInPostRequest - { - Attributes = new ChickenAttributesInPostRequest - { - Name = "Chicken", - NameOfCurrentFarm = null, - Age = 10, - Weight = 30, - TimeAtCurrentFarmInDays = 100, - HasProducedEggs = true - } - } - }; - - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - chicken => chicken.NameOfCurrentFarm)) - { - // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); - } - - [Fact] - public async Task Can_set_default_value_to_ValueType_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new ChickenPostRequestDocument - { - Data = new ChickenDataInPostRequest - { - Attributes = new ChickenAttributesInPostRequest - { - Name = "Chicken", - NameOfCurrentFarm = "Cow and Chicken Farm", - TimeAtCurrentFarmInDays = 100 - } - } - }; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(ChickenUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""chickens"", - ""attributes"": { - ""name"": ""Chicken"", - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""age"": 0, - ""weight"": 0, - ""timeAtCurrentFarmInDays"": 100, - ""hasProducedEggs"": false - } - } -}"); - } - - [Fact] - public async Task Can_exclude_optional_relationships() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = new HenHouseRelationshipsInPostRequest - { - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": { - ""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePatchRequestDocument - { - Data = new HenHouseDataInPatchRequest - { - Id = "1", - Type = HenHouseResourceType.HenHouses, - Relationships = new HenHouseRelationshipsInPatchRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""id"": ""1"", - ""relationships"": { - ""oldestChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Fact] - public async Task Can_clear_nullable_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = new HenHouseRelationshipsInPostRequest - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = null - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HenHouseUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""henHouses"", - ""relationships"": { - ""oldestChicken"": { - ""data"": null - }, - ""firstChicken"": { - ""data"": { - ""type"": ""chickens"", - ""id"": ""1"" - } - }, - ""allChickens"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - }, - ""chickensReadyForLaying"": { - ""data"": [ - { - ""type"": ""chickens"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - Func> action; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - model => model.FirstChicken, model => model.AllChickens, model => model.ChickensReadyForLaying)) - { - // Act - action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - } - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } - - [Theory] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.FirstChicken), "firstChicken")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.AllChickens), "allChickens")] - [InlineData(nameof(HenHouseRelationshipsInPostRequest.ChickensReadyForLaying), "chickensReadyForLaying")] - public async Task Cannot_clear_non_nullable_relationships_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHouseRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestChicken = new NullableToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - FirstChicken = new ToOneChickenInRequest - { - Data = new ChickenIdentifier - { - Id = "1", - Type = ChickenResourceType.Chickens - } - }, - AllChickens = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - }, - ChickensReadyForLaying = new ToManyChickenInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = ChickenResourceType.Chickens - } - } - } - }; - - PropertyInfo relationshipToClearPropertyInfo = relationshipsInPostDocument.GetType().GetProperties().Single(property => property.Name == propertyName); - object relationshipToClear = relationshipToClearPropertyInfo.GetValue(relationshipsInPostDocument)!; - relationshipToClear.SetPropertyToDefaultValue("Data"); - - var requestDocument = new HenHousePostRequestDocument - { - Data = new HenHouseDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonName}'."); - } -} diff --git a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs b/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs deleted file mode 100644 index aef305b63d..0000000000 --- a/test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled/RequestTests.cs +++ /dev/null @@ -1,780 +0,0 @@ -using System.Net; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.SchemaProperties.NullableReferenceTypesEnabled; - -public sealed class RequestTests -{ - private const string CowUrl = "http://localhost/cows"; - private const string CowStableUrl = "http://localhost/cowStables"; - - [Fact] - public async Task Can_exclude_optional_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowPostRequestDocument - { - Data = new CowDataInPostRequest - { - Attributes = new CowAttributesInPostRequest - { - Name = "Cow", - NameOfCurrentFarm = "Cow and Chicken Farm", - Nickname = "Cow", - Weight = 30, - HasProducedMilk = true - } - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cows"", - ""attributes"": { - ""name"": ""Cow"", - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""nickname"": ""Cow"", - ""weight"": 30, - ""hasProducedMilk"": true - } - } -}"); - } - - [Theory] - [InlineData(nameof(CowAttributesInResponse.Name), "name")] - [InlineData(nameof(CowAttributesInResponse.NameOfCurrentFarm), "nameOfCurrentFarm")] - [InlineData(nameof(CowAttributesInResponse.Nickname), "nickname")] - [InlineData(nameof(CowAttributesInResponse.Weight), "weight")] - [InlineData(nameof(CowAttributesInResponse.HasProducedMilk), "hasProducedMilk")] - public async Task Cannot_exclude_required_attribute_when_performing_POST(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var attributesInPostRequest = new CowAttributesInPostRequest - { - Name = "Cow", - NameOfCurrentFarm = "Cow and Chicken Farm", - NameOfPreviousFarm = "Animal Farm", - Nickname = "Cow", - Age = 10, - Weight = 30, - TimeAtCurrentFarmInDays = 100, - HasProducedMilk = true - }; - - attributesInPostRequest.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new CowPostRequestDocument - { - Data = new CowDataInPostRequest - { - Attributes = attributesInPostRequest - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_exclude_attributes_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowPatchRequestDocument - { - Data = new CowDataInPatchRequest - { - Id = "1", - Attributes = new CowAttributesInPatchRequest - { - NameOfPreviousFarm = "Animal Farm", - Age = 10, - TimeAtCurrentFarmInDays = 100 - } - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(1, requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(CowUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cows"", - ""id"": ""1"", - ""attributes"": { - ""nameOfPreviousFarm"": ""Animal Farm"", - ""age"": 10, - ""timeAtCurrentFarmInDays"": 100 - } - } -}"); - } - - [Fact] - public async Task Cannot_exclude_id_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowPatchRequestDocument - { - Data = new CowDataInPatchRequest - { - Attributes = new CowAttributesInPatchRequest - { - Name = "Cow", - NameOfCurrentFarm = "Cow and Chicken Farm", - NameOfPreviousFarm = "Animal Farm", - Nickname = "Cow", - Age = 10, - Weight = 30, - TimeAtCurrentFarmInDays = 100, - HasProducedMilk = true - } - } - }; - - // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(1, requestDocument)); - - // Assert - await action.Should().ThrowAsync(); - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); - } - - [Fact] - public async Task Can_clear_nullable_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowPostRequestDocument - { - Data = new CowDataInPostRequest - { - Attributes = new CowAttributesInPostRequest - { - NameOfPreviousFarm = null, - TimeAtCurrentFarmInDays = null, - Name = "Cow", - NameOfCurrentFarm = "Cow and Chicken Farm", - Nickname = "Cow", - Age = 10, - Weight = 30, - HasProducedMilk = true - } - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - cow => cow.NameOfPreviousFarm, cow => cow.TimeAtCurrentFarmInDays)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cows"", - ""attributes"": { - ""name"": ""Cow"", - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""nameOfPreviousFarm"": null, - ""nickname"": ""Cow"", - ""age"": 10, - ""weight"": 30, - ""timeAtCurrentFarmInDays"": null, - ""hasProducedMilk"": true - } - } -}"); - } - - [Fact] - public async Task Can_set_default_value_to_ValueType_attributes() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowPostRequestDocument - { - Data = new CowDataInPostRequest - { - Attributes = new CowAttributesInPostRequest - { - Name = "Cow", - NameOfCurrentFarm = "Cow and Chicken Farm", - NameOfPreviousFarm = "Animal Farm", - Nickname = "Cow", - TimeAtCurrentFarmInDays = 100 - } - } - }; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cows"", - ""attributes"": { - ""name"": ""Cow"", - ""nameOfCurrentFarm"": ""Cow and Chicken Farm"", - ""nameOfPreviousFarm"": ""Animal Farm"", - ""nickname"": ""Cow"", - ""age"": 0, - ""weight"": 0, - ""timeAtCurrentFarmInDays"": 100, - ""hasProducedMilk"": false - } - } -}"); - } - - [Fact] - public async Task Can_exclude_optional_relationships() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = new CowStableRelationshipsInPostRequest - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowStableUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""relationships"": { - ""oldestCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""firstCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""favoriteCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""allCows"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Theory] - [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_with_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Ignored property '{jsonName}' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Theory] - [InlineData(nameof(CowStableRelationshipsInPostRequest.OldestCow), "oldestCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FirstCow), "firstCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.FavoriteCow), "favoriteCow")] - [InlineData(nameof(CowStableRelationshipsInPostRequest.AllCows), "allCows")] - public async Task Cannot_exclude_required_relationship_when_performing_POST_without_document_registration(string propertyName, string jsonName) - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStableRelationshipsInPostRequest relationshipsInPostDocument = new() - { - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - AlbinoCow = new NullableToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - }; - - relationshipsInPostDocument.SetPropertyToDefaultValue(propertyName); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = relationshipsInPostDocument - } - }; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be($"Cannot write a null value for property '{jsonName}'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_exclude_relationships_that_are_required_for_POST_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePatchRequestDocument - { - Data = new CowStableDataInPatchRequest - { - Id = "1", - Type = CowStableResourceType.CowStables, - Relationships = new CowStableRelationshipsInPatchRequest - { - AlbinoCow = new NullableToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(1, requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(CowStableUrl + "/1"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""id"": ""1"", - ""relationships"": { - ""albinoCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""cowsReadyForMilking"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } - - [Fact] - public async Task Can_clear_nullable_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - var requestDocument = new CowStablePostRequestDocument - { - Data = new CowStableDataInPostRequest - { - Relationships = new CowStableRelationshipsInPostRequest - { - AlbinoCow = new NullableToOneCowInRequest - { - Data = null - }, - OldestCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FirstCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - FavoriteCow = new ToOneCowInRequest - { - Data = new CowIdentifier - { - Id = "1", - Type = CowResourceType.Cows - } - }, - CowsReadyForMilking = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - }, - AllCows = new ToManyCowInRequest - { - Data = new List - { - new() - { - Id = "1", - Type = CowResourceType.Cows - } - } - } - } - } - }; - - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(CowStableUrl); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - wrapper.RequestBody.Should().BeJson(@"{ - ""data"": { - ""type"": ""cowStables"", - ""relationships"": { - ""oldestCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""firstCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""albinoCow"": { - ""data"": null - }, - ""favoriteCow"": { - ""data"": { - ""type"": ""cows"", - ""id"": ""1"" - } - }, - ""cowsReadyForMilking"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - }, - ""allCows"": { - ""data"": [ - { - ""type"": ""cows"", - ""id"": ""1"" - } - ] - } - } - } -}"); - } -} diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj index d6cb1d3002..bbd9209a7b 100644 --- a/test/OpenApiTests/OpenApiTests.csproj +++ b/test/OpenApiTests/OpenApiTests.csproj @@ -6,7 +6,7 @@ - + @@ -17,6 +17,5 @@ - diff --git a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs similarity index 90% rename from test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs rename to test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs index 1b4dfe7c62..f3e39495c3 100644 --- a/test/OpenApiTests/SchemaProperties/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.Configuration; using TestBuildingBlocks; -namespace OpenApiTests.SchemaProperties; +namespace OpenApiTests.ResourceFieldsValidation; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class ModelStateValidationDisabledStartup : OpenApiStartup diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs similarity index 80% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs index 4865d33af9..53c1a17f98 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/Chicken.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs @@ -5,10 +5,10 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] public sealed class Chicken : Identifiable { [Attr] diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs similarity index 80% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs index 979b029717..8c24664eec 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/HenHouse.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs @@ -5,10 +5,10 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] public sealed class HenHouse : Identifiable { [HasOne] diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..4620ebd844 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs @@ -0,0 +1,181 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled"; + } + + [Fact] + public async Task Schema_property_is_nullable_for_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("name").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("age").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("weight").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_nullable_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("hasProducedEggs").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_nullable_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("oldestChicken.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("firstChicken.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("chickensReadyForLaying.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("allChickens.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs similarity index 92% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs index a93f4bf779..6562979252 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; // @formatter:wrap_chained_method_calls chop_always diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs new file mode 100644 index 0000000000..d2d7669b34 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs @@ -0,0 +1,192 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.Required; + +public sealed class ModelStateValidationDisabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_is_not_required_for_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("name"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nameOfCurrentFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("age"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("weight"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("hasProducedEggs"); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("oldestChicken"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain("firstChicken"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("allChickens"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain("chickensReadyForLaying"); + }); + } + + [Fact] + public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..24b01433c2 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs @@ -0,0 +1,191 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.Required; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_is_not_required_for_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("name"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nameOfCurrentFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("age"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("weight"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("hasProducedEggs"); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("oldestChicken"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain("firstChicken"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("allChickens"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => + { + var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredAttributes.Should().Contain("chickensReadyForLaying"); + }); + } + + [Fact] + public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs similarity index 83% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs index 3b5562a0b3..4a09687602 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/Cow.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs @@ -3,10 +3,10 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] public sealed class Cow : Identifiable { [Attr] diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs similarity index 82% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs index b267423501..6b7af3c5d0 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/CowStable.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs @@ -3,10 +3,10 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.SchemaProperties")] +[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] public sealed class CowStable : Identifiable { [HasOne] diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..82b2a94b8c --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs @@ -0,0 +1,245 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled"; + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("name").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_nullable_for_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nameOfPreviousFarm").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nickname").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("age").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("weight").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_nullable_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("hasProducedMilk").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("oldestCow.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("firstCow.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_nullable_for_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("albinoCow.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("favoriteCow.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("cowsReadyForMilking.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_is_not_nullable_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("allCows.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs similarity index 93% rename from test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs rename to test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs index e83298f28d..d8ad163bdd 100644 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; // @formatter:wrap_chained_method_calls chop_always diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs new file mode 100644 index 0000000000..17ac968dee --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs @@ -0,0 +1,252 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.Required; + +public sealed class ModelStateValidationDisabledTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> + _testContext; + + public ModelStateValidationDisabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_is_required_for_non_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("name"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_non_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nameOfCurrentFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("nameOfPreviousFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nickname"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("age"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("weight"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("hasProducedMilk"); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableAttributesInPatchRequest.required"); + } + + [Fact] + public async Task Schema_property_is_required_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("oldestCow"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("firstCow"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("albinoCow"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("favoriteCow"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("cowsReadyForMilking"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("allCows"); + }); + } + + [Fact] + public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs new file mode 100644 index 0000000000..24146e7bba --- /dev/null +++ b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs @@ -0,0 +1,251 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.Required; + +public sealed class ModelStateValidationEnabledTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public ModelStateValidationEnabledTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_is_required_for_non_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("name"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_non_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nameOfCurrentFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("nameOfPreviousFarm"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_reference_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("nickname"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("age"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("weight"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("hasProducedMilk"); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableAttributesInPatchRequest.required"); + } + + [Fact] + public async Task Schema_property_is_required_for_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("oldestCow"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("firstCow"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("albinoCow"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_nullable_has_one_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("favoriteCow"); + }); + } + + [Fact] + public async Task Schema_property_is_not_required_for_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().NotContain("cowsReadyForMilking"); + }); + } + + [Fact] + public async Task Schema_property_is_required_for_required_has_many_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => + { + var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); + + requiredProperties.Should().Contain("allCows"); + }); + } + + [Fact] + public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs deleted file mode 100644 index 0b88c3f88b..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Theory] - [InlineData("nameOfCurrentFarm")] - [InlineData("weight")] - [InlineData("hasProducedEggs")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("name")] - [InlineData("age")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Theory] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestChicken")] - [InlineData("allChickens")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs deleted file mode 100644 index bd31a1f25a..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Theory] - [InlineData("nameOfCurrentFarm")] - [InlineData("weight")] - [InlineData("hasProducedEggs")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string attributeName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain(attributeName); - }); - } - - [Theory] - [InlineData("name")] - [InlineData("age")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string attributeName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(attributeName); - }); - } - - [Fact] - public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Theory] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestChicken")] - [InlineData("allChickens")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs deleted file mode 100644 index dca51da02b..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesDisabled/NullabilityTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesDisabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesDisabled"; - } - - [Theory] - [InlineData("name")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attribute_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("nameOfCurrentFarm")] - [InlineData("age")] - [InlineData("weight")] - [InlineData("hasProducedEggs")] - public async Task Property_in_schema_for_attribute_of_should_not_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("oldestChicken")] - public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("allChickens")] - [InlineData("firstChicken")] - [InlineData("chickensReadyForLaying")] - public async Task Data_property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs deleted file mode 100644 index fb83c4ea5f..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Theory] - [InlineData("nameOfCurrentFarm")] - [InlineData("nickname")] - [InlineData("weight")] - [InlineData("hasProducedMilk")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("name")] - [InlineData("age")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Theory] - [InlineData("firstCow")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("cowsReadyForMilking")] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs deleted file mode 100644 index d45937a580..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Theory] - [InlineData("name")] - [InlineData("nameOfCurrentFarm")] - [InlineData("nickname")] - [InlineData("weight")] - [InlineData("hasProducedMilk")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("age")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attributes_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_attributes_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("firstCow")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain(propertyName); - }); - } - - [Theory] - [InlineData("cowsReadyForMilking")] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationships_in_POST_request_should_not_be_required(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain(propertyName); - }); - } - - [Fact] - public async Task Schema_for_relationships_in_PATCH_request_should_have_no_required_properties() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs deleted file mode 100644 index 5652c61f33..0000000000 --- a/test/OpenApiTests/SchemaProperties/NullableReferenceTypesEnabled/NullabilityTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.SchemaProperties.NullableReferenceTypesEnabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/SchemaProperties/NullableReferenceTypesEnabled"; - } - - [Theory] - [InlineData("nameOfPreviousFarm")] - [InlineData("timeAtCurrentFarmInDays")] - public async Task Property_in_schema_for_attribute_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(propertyName).With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Theory] - [InlineData("name")] - [InlineData("nameOfCurrentFarm")] - [InlineData("nickname")] - [InlineData("age")] - [InlineData("weight")] - [InlineData("hasProducedMilk")] - public async Task Property_in_schema_for_attribute_of_resource_should_not_be_nullable(string attributeName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath(attributeName).With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Theory] - [InlineData("albinoCow")] - public async Task Property_in_schema_for_relationship_of_resource_should_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Theory] - [InlineData("oldestCow")] - [InlineData("firstCow")] - [InlineData("cowsReadyForMilking")] - [InlineData("allCows")] - [InlineData("favoriteCow")] - public async Task Data_property_in_schema_for_relationship_of_resource_should_not_be_nullable(string propertyName) - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath($"{propertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/JsonElementExtensions.cs b/test/TestBuildingBlocks/JsonElementExtensions.cs similarity index 87% rename from test/OpenApiTests/JsonElementExtensions.cs rename to test/TestBuildingBlocks/JsonElementExtensions.cs index 3649127917..ee5c362489 100644 --- a/test/OpenApiTests/JsonElementExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementExtensions.cs @@ -3,11 +3,10 @@ using FluentAssertions; using FluentAssertions.Execution; using JetBrains.Annotations; -using TestBuildingBlocks; -namespace OpenApiTests; +namespace TestBuildingBlocks; -internal static class JsonElementExtensions +public static class JsonElementExtensions { public static JsonElementAssertions Should(this JsonElement source) { @@ -33,6 +32,12 @@ public static void ShouldBeString(this JsonElement source, string value) source.GetString().Should().Be(value); } + public static void ShouldBeInteger(this JsonElement source, int value) + { + source.ValueKind.Should().Be(JsonValueKind.Number); + source.GetInt32().Should().Be(value); + } + public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElement source, string value) { string schemaReferenceId = GetSchemaReferenceId(source); @@ -63,7 +68,7 @@ public sealed class SchemaReferenceIdContainer { public string SchemaReferenceId { get; } - public SchemaReferenceIdContainer(string schemaReferenceId) + internal SchemaReferenceIdContainer(string schemaReferenceId) { SchemaReferenceId = schemaReferenceId; } @@ -71,7 +76,7 @@ public SchemaReferenceIdContainer(string schemaReferenceId) public sealed class JsonElementAssertions : JsonElementAssertions { - public JsonElementAssertions(JsonElement subject) + internal JsonElementAssertions(JsonElement subject) : base(subject) { } diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index ce8c54ef3b..55e2830bb9 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -8,6 +8,7 @@ + From a2e7a96dcd5c380c6da677b461acffa259ca9fdb Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 28 Dec 2022 23:59:11 +0100 Subject: [PATCH 21/66] Add two missing 'Act' comments --- .../NullableReferenceTypesDisabled/RequestTests.cs | 2 ++ .../NullableReferenceTypesEnabled/RequestTests.cs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs index 35499a2cc0..75d086e6ce 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs @@ -372,6 +372,7 @@ public async Task Can_clear_has_one_relationship() HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); requestDocument.Data.Relationships.OldestChicken.Data = null; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); // Assert @@ -612,6 +613,7 @@ public async Task Relationships_required_in_POST_request_are_not_required_in_PAT requestDocument.Data.Relationships.ChickensReadyForLaying = default!; requestDocument.Data.Relationships.AllChickens = default!; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs index 2a509fef2a..0c36b0a137 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs +++ b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs @@ -458,6 +458,7 @@ public async Task Can_clear_nullable_has_one_relationship() CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); requestDocument.Data.Relationships.AlbinoCow.Data = null; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert @@ -487,6 +488,7 @@ public async Task Can_exclude_nullable_has_one_relationship() CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); requestDocument.Data.Relationships.AlbinoCow = default!; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert @@ -561,6 +563,7 @@ public async Task Can_exclude_has_many_relationship() CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); requestDocument.Data.Relationships.CowsReadyForMilking = default!; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); // Assert @@ -700,6 +703,7 @@ public async Task Relationships_required_in_POST_request_are_not_required_in_PAT requestDocument.Data.Relationships.FavoriteCow = default!; requestDocument.Data.Relationships.AllCows = default!; + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert From 1815decf1d653e49f3f6cbf7fb27855778604843 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 3 Jan 2023 10:20:50 +0100 Subject: [PATCH 22/66] More elaborate testing -> in sync with latest version of nullability/required table -> introduces ResourceFieldValidationMetadataProvider -> Fix test in legacy projects -> Reusable faker building block for OpenApiClient related concerns --- .../IJsonApiClient.cs | 2 +- .../JsonApiClient.cs | 72 +- ...onApiActionDescriptorCollectionProvider.cs | 5 +- .../JsonApiEndpointMetadataProvider.cs | 18 +- .../NonPrimaryDocumentTypeFactory.cs | 11 +- .../JsonApiObjects/RelationshipTypeFactory.cs | 11 +- .../ResourceFieldAttributeExtensions.cs | 39 - ...ResourceFieldValidationMetadataProvider.cs | 92 + .../ServiceCollectionExtensions.cs | 7 +- .../JsonApiSchemaGenerator.cs | 7 +- .../ResourceFieldObjectSchemaBuilder.cs | 44 +- .../ResourceObjectSchemaGenerator.cs | 4 +- test/OpenApiClientTests/FakerFactory.cs | 41 + ...equestDocumentRegistrationLifetimeTests.cs | 26 +- .../LegacyClient/RequestTests.cs | 4 +- .../LegacyClient/ResponseTests.cs | 10 +- .../LegacyClient/swagger.g.json | 14 + .../CamelCase/swagger.g.json | 9 + .../KebabCase/swagger.g.json | 9 + .../PascalCase/swagger.g.json | 9 + test/OpenApiClientTests/ObjectExtensions.cs | 37 + test/OpenApiClientTests/OpenApiClientTests.cs | 20 + .../OpenApiClientTests.csproj | 36 +- .../CreateResourceTests.cs | 528 +++++ .../GeneratedCode/NrtOffMsvOffClient.cs | 14 + .../NullabilityTests.cs | 23 + .../ResourceFieldValidationFakers.cs | 25 + .../UpdateResourceTests.cs | 146 ++ .../swagger.g.json | 1677 ++++++++++++++++ .../CreateResourceTests.cs | 569 ++++++ .../GeneratedCode/NrtOffMsvOnClient.cs} | 4 +- .../NullabilityTests.cs | 23 + .../ResourceFieldValidationFakers.cs | 29 + .../UpdateResourceTests.cs | 146 ++ .../swagger.g.json | 1744 +++++++++++++++++ .../CreateResourceTests.cs | 613 ++++++ .../GeneratedCode/NrtOnMsvOffClient.cs | 14 + .../NullabilityTests.cs | 25 + .../ResourceFieldValidationFakers.cs | 29 + .../UpdateResourceTests.cs | 156 ++ .../swagger.g.json | 1507 ++++++-------- .../CreateResourceTests.cs | 613 ++++++ .../GeneratedCode/NrtOnMsvOnClient.cs} | 4 +- .../NullabilityTests.cs | 25 + .../ResourceFieldValidationFakers.cs | 29 + .../UpdateResourceTests.cs | 156 ++ .../swagger.g.json | 1436 +++++++------- .../NullabilityTests.cs | 23 - .../NullableReferenceTypesDisabledFaker.cs | 124 -- .../RequestTests.cs | 638 ------ .../NullabilityTests.cs | 25 - .../NullableReferenceTypesEnabledFaker.cs | 132 -- .../RequestTests.cs | 728 ------- .../LegacyOpenApiIntegration/swagger.json | 14 + test/OpenApiTests/OpenApiTestContext.cs | 1 - .../ResourceFieldValidation/EmptyResource.cs | 9 + .../ModelStateValidationDisabledStartup.cs | 2 +- .../NullabilityTests.cs | 182 ++ .../RequiredTests.cs | 180 ++ .../NullabilityTests.cs | 67 +- .../RequiredTests.cs | 181 ++ .../NrtDisabledResource.cs | 48 + ...NullableReferenceTypesDisabledDbContext.cs | 35 + .../NullabilityTests.cs | 100 + .../RequiredTests.cs | 100 + .../NullabilityTests.cs | 244 +++ .../RequiredTests.cs | 234 +++ .../NrtEnabledResource.cs | 60 + .../NullableReferenceTypesEnabledDbContext.cs | 41 + .../NullableReferenceTypesDisabled/Chicken.cs | 34 - .../HenHouse.cs | 27 - ...NullableReferenceTypesDisabledDbContext.cs | 36 - .../ModelStateValidationDisabledTests.cs | 192 -- .../ModelStateValidationEnabledTests.cs | 191 -- .../NullableReferenceTypesEnabled/Cow.cs | 39 - .../CowStable.cs | 32 - .../NullabilityTests.cs | 245 --- .../NullableReferenceTypesEnabledDbContext.cs | 42 - .../ModelStateValidationDisabledTests.cs | 252 --- .../ModelStateValidationEnabledTests.cs | 251 --- .../JsonElementExtensions.cs | 16 + 81 files changed, 9828 insertions(+), 4759 deletions(-) delete mode 100644 src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs create mode 100644 src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs create mode 100644 test/OpenApiClientTests/FakerFactory.cs create mode 100644 test/OpenApiClientTests/ObjectExtensions.cs create mode 100644 test/OpenApiClientTests/OpenApiClientTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs rename test/OpenApiClientTests/{ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs => ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs} (58%) create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs rename test/OpenApiClientTests/{ResourceFieldsValidation/NullableReferenceTypesEnabled => ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled}/swagger.g.json (67%) create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs rename test/OpenApiClientTests/{ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs => ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs} (59%) create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs create mode 100644 test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs rename test/OpenApiClientTests/{ResourceFieldsValidation/NullableReferenceTypesDisabled => ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled}/swagger.g.json (68%) delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs delete mode 100644 test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs rename test/OpenApiTests/{ResourceFieldsValidation => ResourceFieldValidation}/ModelStateValidationDisabledStartup.cs (90%) create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs rename test/OpenApiTests/{ResourceFieldsValidation/NullableReferenceTypesDisabled => ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled}/NullabilityTests.cs (56%) create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs delete mode 100644 test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs diff --git a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs index 18c90fdfd2..5cb98a7b9a 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs @@ -35,7 +35,7 @@ public interface IJsonApiClient /// An to clear the current registration. For efficient memory usage, it is recommended to wrap calls to this method in a /// using statement, so the registrations are cleaned up after executing the request. /// - IDisposable OmitDefaultValuesForAttributesInRequestDocument(TRequestDocument requestDocument, + IDisposable WithPartialAttributeSerialization(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) where TRequestDocument : class; } diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index f6d9dbdb2d..f119396747 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -24,7 +24,7 @@ protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings) } /// - public IDisposable OmitDefaultValuesForAttributesInRequestDocument(TRequestDocument requestDocument, + public IDisposable WithPartialAttributeSerialization(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) where TRequestDocument : class { @@ -40,12 +40,12 @@ public IDisposable OmitDefaultValuesForAttributesInRequestDocument article.Title'."); + throw new ArgumentException( + $"The expression '{nameof(alwaysIncludedAttributeSelectors)}' should select a single property. For example: 'article => article.Title'."); } } - _jsonApiJsonConverter.RegisterRequestDocumentForAttributesOmission(requestDocument, - new AttributesObjectInfo(attributeNames, typeof(TAttributesObject))); + _jsonApiJsonConverter.RegisterDocument(requestDocument, new AttributeNamesContainer(attributeNames, typeof(TAttributesObject))); return new RequestDocumentRegistrationScope(_jsonApiJsonConverter, requestDocument); } @@ -69,15 +69,15 @@ private static Expression RemoveConvert(Expression expression) private sealed class JsonApiJsonConverter : JsonConverter { - private readonly Dictionary _attributesObjectInfoByRequestDocument = new(); + private readonly Dictionary _alwaysIncludedAttributesByRequestDocument = new(); private readonly Dictionary> _requestDocumentsByType = new(); private SerializationScope? _serializationScope; public override bool CanRead => false; - public void RegisterRequestDocumentForAttributesOmission(object requestDocument, AttributesObjectInfo attributesObjectInfo) + public void RegisterDocument(object requestDocument, AttributeNamesContainer alwaysIncludedAttributes) { - _attributesObjectInfoByRequestDocument[requestDocument] = attributesObjectInfo; + _alwaysIncludedAttributesByRequestDocument[requestDocument] = alwaysIncludedAttributes; Type requestDocumentType = requestDocument.GetType(); @@ -91,9 +91,9 @@ public void RegisterRequestDocumentForAttributesOmission(object requestDocument, public void RemoveRegistration(object requestDocument) { - if (_attributesObjectInfoByRequestDocument.ContainsKey(requestDocument)) + if (_alwaysIncludedAttributesByRequestDocument.ContainsKey(requestDocument)) { - _attributesObjectInfoByRequestDocument.Remove(requestDocument); + _alwaysIncludedAttributesByRequestDocument.Remove(requestDocument); Type requestDocumentType = requestDocument.GetType(); _requestDocumentsByType[requestDocumentType].Remove(requestDocument); @@ -136,7 +136,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer } else { - AttributesObjectInfo? attributesObjectInfo = _serializationScope.AttributesObjectInScope; + AttributeNamesContainer? attributesObjectInfo = _serializationScope.AttributesObjectInScope; AssertObjectMatchesSerializationScope(attributesObjectInfo, value); @@ -158,7 +158,7 @@ private void SerializeRequestDocument(JsonWriter writer, object value, JsonSeria { _serializationScope = new SerializationScope(); - if (_attributesObjectInfoByRequestDocument.TryGetValue(value, out AttributesObjectInfo? attributesObjectInfo)) + if (_alwaysIncludedAttributesByRequestDocument.TryGetValue(value, out AttributeNamesContainer? attributesObjectInfo)) { _serializationScope.AttributesObjectInScope = attributesObjectInfo; } @@ -173,17 +173,18 @@ private void SerializeRequestDocument(JsonWriter writer, object value, JsonSeria } } - private static void AssertObjectMatchesSerializationScope([SysNotNull] AttributesObjectInfo? attributesObjectInfo, object value) + private static void AssertObjectMatchesSerializationScope([SysNotNull] AttributeNamesContainer? attributesObjectInfo, object value) { Type objectType = value.GetType(); - if (attributesObjectInfo == null || !attributesObjectInfo.MatchesType(objectType)) + if (attributesObjectInfo == null || !attributesObjectInfo.MatchesAttributesObjectType(objectType)) { throw new UnreachableCodeException(); } } - private static void SerializeAttributesObject(AttributesObjectInfo alwaysIncludedAttributes, JsonWriter writer, object value, JsonSerializer serializer) + private static void SerializeAttributesObject(AttributeNamesContainer alwaysIncludedAttributes, JsonWriter writer, object value, + JsonSerializer serializer) { AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes, writer); @@ -191,13 +192,13 @@ private static void SerializeAttributesObject(AttributesObjectInfo alwaysInclude serializer.Serialize(writer, value); } - private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributesObjectInfo alwaysIncludedAttributes, JsonWriter jsonWriter) + private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributeNamesContainer alwaysIncludedAttributes, JsonWriter jsonWriter) { PropertyInfo[] propertyInfos = value.GetType().GetProperties(); foreach (PropertyInfo attributesPropertyInfo in propertyInfos) { - bool isExplicitlyIncluded = alwaysIncludedAttributes.IsAttributeMarkedForInclusion(attributesPropertyInfo.Name); + bool isExplicitlyIncluded = alwaysIncludedAttributes.ContainsAttribute(attributesPropertyInfo.Name); if (isExplicitlyIncluded) { @@ -212,7 +213,7 @@ private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInf { JsonPropertyAttribute jsonPropertyForAttribute = attribute.GetCustomAttributes().Single(); - if (jsonPropertyForAttribute.Required != Required.Always) + if (jsonPropertyForAttribute.Required is not (Required.Always or Required.AllowNull)) { return; } @@ -221,8 +222,7 @@ private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInf if (isPropertyIgnored) { - throw new JsonSerializationException( - $"Ignored property '{jsonPropertyForAttribute.PropertyName}' must have a value because it is required. Path '{path}'."); + throw new InvalidOperationException($"The following property should not be omitted: {path}.{jsonPropertyForAttribute.PropertyName}."); } } @@ -248,7 +248,7 @@ private static bool DefaultValueEqualsCurrentValue(PropertyInfo propertyInfo, ob private sealed class SerializationScope { private bool _isFirstAttemptToConvertAttributes = true; - public AttributesObjectInfo? AttributesObjectInScope { get; set; } + public AttributeNamesContainer? AttributesObjectInScope { get; set; } public bool ShouldConvertAsAttributesObject(Type type) { @@ -257,7 +257,7 @@ public bool ShouldConvertAsAttributesObject(Type type) return false; } - if (!AttributesObjectInScope.MatchesType(type)) + if (!AttributesObjectInScope.MatchesAttributesObjectType(type)) { return false; } @@ -267,26 +267,26 @@ public bool ShouldConvertAsAttributesObject(Type type) } } - private sealed class AttributesObjectInfo + private sealed class AttributeNamesContainer { - private readonly ISet _attributesMarkedForInclusion; + private readonly ISet _attributeNames; private readonly Type _attributesObjectType; - public AttributesObjectInfo(ISet attributesMarkedForInclusion, Type attributesObjectType) + public AttributeNamesContainer(ISet attributeNames, Type attributesObjectType) { - ArgumentGuard.NotNull(attributesMarkedForInclusion); + ArgumentGuard.NotNull(attributeNames); ArgumentGuard.NotNull(attributesObjectType); - _attributesMarkedForInclusion = attributesMarkedForInclusion; + _attributeNames = attributeNames; _attributesObjectType = attributesObjectType; } - public bool IsAttributeMarkedForInclusion(string name) + public bool ContainsAttribute(string name) { - return _attributesMarkedForInclusion.Contains(name); + return _attributeNames.Contains(name); } - public bool MatchesType(Type type) + public bool MatchesAttributesObjectType(Type type) { return _attributesObjectType == type; } @@ -314,22 +314,24 @@ public void Dispose() private sealed class JsonApiAttributeContractResolver : DefaultContractResolver { - private readonly AttributesObjectInfo _attributesObjectInfo; + private readonly AttributeNamesContainer _alwaysIncludedAttributes; - public JsonApiAttributeContractResolver(AttributesObjectInfo attributesObjectInfo) + public JsonApiAttributeContractResolver(AttributeNamesContainer alwaysIncludedAttributes) { - ArgumentGuard.NotNull(attributesObjectInfo); + ArgumentGuard.NotNull(alwaysIncludedAttributes); - _attributesObjectInfo = attributesObjectInfo; + _alwaysIncludedAttributes = alwaysIncludedAttributes; } protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); - if (_attributesObjectInfo.MatchesType(property.DeclaringType!)) + bool canOmitAttribute = property.Required != Required.Always; + + if (canOmitAttribute && _alwaysIncludedAttributes.MatchesAttributesObjectType(property.DeclaringType!)) { - if (_attributesObjectInfo.IsAttributeMarkedForInclusion(property.UnderlyingName!)) + if (_alwaysIncludedAttributes.ContainsAttribute(property.UnderlyingName!)) { property.NullValueHandling = NullValueHandling.Include; property.DefaultValueHandling = DefaultValueHandling.Include; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs index 98ae9938b4..16b173dd17 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiActionDescriptorCollectionProvider.cs @@ -22,13 +22,14 @@ internal sealed class JsonApiActionDescriptorCollectionProvider : IActionDescrip public ActionDescriptorCollection ActionDescriptors => GetActionDescriptors(); - public JsonApiActionDescriptorCollectionProvider(IControllerResourceMapping controllerResourceMapping, IActionDescriptorCollectionProvider defaultProvider) + public JsonApiActionDescriptorCollectionProvider(IControllerResourceMapping controllerResourceMapping, IActionDescriptorCollectionProvider defaultProvider, + ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(controllerResourceMapping); ArgumentGuard.NotNull(defaultProvider); _defaultProvider = defaultProvider; - _jsonApiEndpointMetadataProvider = new JsonApiEndpointMetadataProvider(controllerResourceMapping); + _jsonApiEndpointMetadataProvider = new JsonApiEndpointMetadataProvider(controllerResourceMapping, resourceFieldValidationMetadataProvider); } private ActionDescriptorCollection GetActionDescriptors() diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs index d0fe2f1234..0dcb87cabb 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs @@ -15,11 +15,13 @@ internal sealed class JsonApiEndpointMetadataProvider { private readonly IControllerResourceMapping _controllerResourceMapping; private readonly EndpointResolver _endpointResolver = new(); + private readonly NonPrimaryDocumentTypeFactory _nonPrimaryDocumentTypeFactory; - public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping) + public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping, + ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(controllerResourceMapping); - + _nonPrimaryDocumentTypeFactory = new NonPrimaryDocumentTypeFactory(resourceFieldValidationMetadataProvider); _controllerResourceMapping = controllerResourceMapping; } @@ -85,12 +87,12 @@ private static PrimaryRequestMetadata GetPatchRequestMetadata(Type resourceClrTy return new PrimaryRequestMetadata(documentType); } - private static RelationshipRequestMetadata GetRelationshipRequestMetadata(IEnumerable relationships, bool ignoreHasOneRelationships) + private RelationshipRequestMetadata GetRelationshipRequestMetadata(IEnumerable relationships, bool ignoreHasOneRelationships) { IEnumerable relationshipsOfEndpoint = ignoreHasOneRelationships ? relationships.OfType() : relationships; IDictionary requestDocumentTypesByRelationshipName = relationshipsOfEndpoint.ToDictionary(relationship => relationship.PublicName, - NonPrimaryDocumentTypeFactory.Instance.GetForRelationshipRequest); + _nonPrimaryDocumentTypeFactory.GetForRelationshipRequest); return new RelationshipRequestMetadata(requestDocumentTypesByRelationshipName); } @@ -129,18 +131,18 @@ private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type resourceC return new PrimaryResponseMetadata(documentType); } - private static SecondaryResponseMetadata GetSecondaryResponseMetadata(IEnumerable relationships) + private SecondaryResponseMetadata GetSecondaryResponseMetadata(IEnumerable relationships) { IDictionary responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName, - NonPrimaryDocumentTypeFactory.Instance.GetForSecondaryResponse); + _nonPrimaryDocumentTypeFactory.GetForSecondaryResponse); return new SecondaryResponseMetadata(responseDocumentTypesByRelationshipName); } - private static RelationshipResponseMetadata GetRelationshipResponseMetadata(IEnumerable relationships) + private RelationshipResponseMetadata GetRelationshipResponseMetadata(IEnumerable relationships) { IDictionary responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName, - NonPrimaryDocumentTypeFactory.Instance.GetForRelationshipResponse); + _nonPrimaryDocumentTypeFactory.GetForRelationshipResponse); return new RelationshipResponseMetadata(responseDocumentTypesByRelationshipName); } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs index 95d7e2f6e5..387c181f8b 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs @@ -15,10 +15,13 @@ internal sealed class NonPrimaryDocumentTypeFactory private static readonly DocumentOpenTypes RelationshipResponseDocumentOpenTypes = new(typeof(ResourceIdentifierCollectionResponseDocument<>), typeof(NullableResourceIdentifierResponseDocument<>), typeof(ResourceIdentifierResponseDocument<>)); - public static NonPrimaryDocumentTypeFactory Instance { get; } = new(); + private readonly ResourceFieldValidationMetadataProvider _resourceFieldValidationMetadataProvider; - private NonPrimaryDocumentTypeFactory() + public NonPrimaryDocumentTypeFactory(ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { + ArgumentGuard.NotNull(resourceFieldValidationMetadataProvider); + + _resourceFieldValidationMetadataProvider = resourceFieldValidationMetadataProvider; } public Type GetForSecondaryResponse(RelationshipAttribute relationship) @@ -42,13 +45,13 @@ public Type GetForRelationshipResponse(RelationshipAttribute relationship) return Get(relationship, RelationshipResponseDocumentOpenTypes); } - private static Type Get(RelationshipAttribute relationship, DocumentOpenTypes types) + private Type Get(RelationshipAttribute relationship, DocumentOpenTypes types) { // @formatter:nested_ternary_style expanded Type documentOpenType = relationship is HasManyAttribute ? types.ManyDataOpenType - : relationship.IsNullable() + : _resourceFieldValidationMetadataProvider.IsNullable(relationship) ? types.NullableSingleDataOpenType : types.SingleDataOpenType; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs index 7593c9f9f5..06efd7622f 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs @@ -5,17 +5,20 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects; internal sealed class RelationshipTypeFactory { - public static RelationshipTypeFactory Instance { get; } = new(); + private readonly ResourceFieldValidationMetadataProvider _resourceFieldValidationMetadataProvider; + private readonly NonPrimaryDocumentTypeFactory _nonPrimaryDocumentTypeFactory; - private RelationshipTypeFactory() + public RelationshipTypeFactory(ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { + _nonPrimaryDocumentTypeFactory = new NonPrimaryDocumentTypeFactory(resourceFieldValidationMetadataProvider); + _resourceFieldValidationMetadataProvider = resourceFieldValidationMetadataProvider; } public Type GetForRequest(RelationshipAttribute relationship) { ArgumentGuard.NotNull(relationship); - return NonPrimaryDocumentTypeFactory.Instance.GetForRelationshipRequest(relationship); + return _nonPrimaryDocumentTypeFactory.GetForRelationshipRequest(relationship); } public Type GetForResponse(RelationshipAttribute relationship) @@ -26,7 +29,7 @@ public Type GetForResponse(RelationshipAttribute relationship) Type relationshipDataOpenType = relationship is HasManyAttribute ? typeof(ToManyRelationshipInResponse<>) - : relationship.IsNullable() + : _resourceFieldValidationMetadataProvider.IsNullable(relationship) ? typeof(NullableToOneRelationshipInResponse<>) : typeof(ToOneRelationshipInResponse<>); diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs deleted file mode 100644 index b1274cbe97..0000000000 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldAttributeExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Reflection; -using JsonApiDotNetCore.Resources.Annotations; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JsonApiDotNetCore.OpenApi; - -internal static class ResourceFieldAttributeExtensions -{ - public static bool IsNullable(this ResourceFieldAttribute source) - { - bool hasRequiredAttribute = source.Property.HasAttribute(); - - if (hasRequiredAttribute) - { - // Reflects the following cases, independent of NRT setting - // `[Required] int? Number` => not nullable - // `[Required] int Number` => not nullable - // `[Required] string Text` => not nullable - // `[Required] string? Text` => not nullable - // `[Required] string Text` => not nullable - return false; - } - - NullabilityInfoContext nullabilityContext = new(); - NullabilityInfo nullabilityInfo = nullabilityContext.Create(source.Property); - - // Reflects the following cases: - // Independent of NRT: - // `int? Number` => nullable - // `int Number` => not nullable - // If NRT is enabled: - // `string? Text` => nullable - // `string Text` => not nullable - // If NRT is disabled: - // `string Text` => nullable - return nullabilityInfo.ReadState is not NullabilityState.NotNull; - } -} diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs new file mode 100644 index 0000000000..bfbe32bb31 --- /dev/null +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs @@ -0,0 +1,92 @@ +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources.Annotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace JsonApiDotNetCore.OpenApi; + +internal sealed class ResourceFieldValidationMetadataProvider +{ + private readonly bool _validateModelState; + private readonly NullabilityInfoContext _nullabilityContext; + private readonly IModelMetadataProvider _modelMetadataProvider; + + public ResourceFieldValidationMetadataProvider(IJsonApiOptions options, IModelMetadataProvider modelMetadataProvider) + { + ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(modelMetadataProvider); + + _validateModelState = options.ValidateModelState; + _modelMetadataProvider = modelMetadataProvider; + _nullabilityContext = new NullabilityInfoContext(); + } + + public bool IsNullable(ResourceFieldAttribute field) + { + ArgumentGuard.NotNull(field); + + if (field is HasManyAttribute) + { + return false; + } + + bool hasRequiredAttribute = field.Property.HasAttribute(); + NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); + + if (field is HasManyAttribute) + { + return false; + } + + if (hasRequiredAttribute && _validateModelState && nullabilityInfo.ReadState != NullabilityState.NotNull) + { + return false; + } + + return nullabilityInfo.ReadState != NullabilityState.NotNull; + } + + public bool IsRequired(ResourceFieldAttribute field) + { + ArgumentGuard.NotNull(field); + + bool hasRequiredAttribute = field.Property.HasAttribute(); + + if (!_validateModelState) + { + return hasRequiredAttribute; + } + + if (field is HasManyAttribute) + { + return false; + } + + bool isNotNull = HasNullabilityStateNotNull(field); + bool isRequiredValueType = field.Property.PropertyType.IsValueType && hasRequiredAttribute && isNotNull; + + if (isRequiredValueType) + { + return false; + } + + return IsModelStateValidationRequired(field); + } + + private bool IsModelStateValidationRequired(ResourceFieldAttribute field) + { + ModelMetadata resourceFieldModelMetadata = _modelMetadataProvider.GetMetadataForProperties(field.Type.ClrType) + .Single(modelMetadata => modelMetadata.PropertyName! == field.Property.Name); + + return resourceFieldModelMetadata.ValidatorMetadata.Any(validatorMetadata => validatorMetadata is RequiredAttribute); + } + + private bool HasNullabilityStateNotNull(ResourceFieldAttribute field) + { + NullabilityInfo resourceFieldNullabilityInfo = _nullabilityContext.Create(field.Property); + bool hasNullabilityStateNotNull = resourceFieldNullabilityInfo is { ReadState: NullabilityState.NotNull, WriteState: NullabilityState.NotNull }; + return hasNullabilityStateNotNull; + } +} diff --git a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs index f223c7d994..def25eb0dd 100644 --- a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs @@ -34,13 +34,18 @@ public static void AddOpenApi(this IServiceCollection services, IMvcCoreBuilder private static void AddCustomApiExplorer(IServiceCollection services, IMvcCoreBuilder mvcBuilder) { + services.AddSingleton(); + services.AddSingleton(provider => { var controllerResourceMapping = provider.GetRequiredService(); var actionDescriptorCollectionProvider = provider.GetRequiredService(); var apiDescriptionProviders = provider.GetRequiredService>(); + var resourceFieldValidationMetadataProvider = provider.GetRequiredService(); + + JsonApiActionDescriptorCollectionProvider descriptorCollectionProviderWrapper = + new(controllerResourceMapping, actionDescriptorCollectionProvider, resourceFieldValidationMetadataProvider); - JsonApiActionDescriptorCollectionProvider descriptorCollectionProviderWrapper = new(controllerResourceMapping, actionDescriptorCollectionProvider); return new ApiDescriptionGroupCollectionProvider(descriptorCollectionProviderWrapper, apiDescriptionProviders); }); diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs index 2df640379a..1e8efa8452 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs @@ -39,7 +39,8 @@ internal sealed class JsonApiSchemaGenerator : ISchemaGenerator private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator; private readonly SchemaRepositoryAccessor _schemaRepositoryAccessor = new(); - public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options) + public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options, + ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(defaultSchemaGenerator); ArgumentGuard.NotNull(resourceGraph); @@ -47,7 +48,9 @@ public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceG _defaultSchemaGenerator = defaultSchemaGenerator; _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(_schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); - _resourceObjectSchemaGenerator = new ResourceObjectSchemaGenerator(defaultSchemaGenerator, resourceGraph, options, _schemaRepositoryAccessor); + + _resourceObjectSchemaGenerator = new ResourceObjectSchemaGenerator(defaultSchemaGenerator, resourceGraph, options, _schemaRepositoryAccessor, + resourceFieldValidationMetadataProvider); } public OpenApiSchema GenerateSchema(Type modelType, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null, diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 7c9cb75a15..678229864c 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,5 +1,3 @@ -using System.ComponentModel.DataAnnotations; -using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; @@ -29,27 +27,31 @@ internal sealed class ResourceFieldObjectSchemaBuilder private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; private readonly SchemaGenerator _defaultSchemaGenerator; private readonly ResourceTypeSchemaGenerator _resourceTypeSchemaGenerator; - private readonly IJsonApiOptions _options; private readonly SchemaRepository _resourceSchemaRepository = new(); private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator; private readonly IDictionary _schemasForResourceFields; + private readonly ResourceFieldValidationMetadataProvider _resourceFieldValidationMetadataProvider; + private readonly RelationshipTypeFactory _relationshipTypeFactory; public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, - SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, IJsonApiOptions options) + SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, IJsonApiOptions options, + ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(resourceTypeInfo); ArgumentGuard.NotNull(schemaRepositoryAccessor); ArgumentGuard.NotNull(defaultSchemaGenerator); ArgumentGuard.NotNull(resourceTypeSchemaGenerator); ArgumentGuard.NotNull(options); + ArgumentGuard.NotNull(resourceFieldValidationMetadataProvider); _resourceTypeInfo = resourceTypeInfo; _schemaRepositoryAccessor = schemaRepositoryAccessor; _defaultSchemaGenerator = defaultSchemaGenerator; _resourceTypeSchemaGenerator = resourceTypeSchemaGenerator; - _options = options; + _resourceFieldValidationMetadataProvider = resourceFieldValidationMetadataProvider; _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); + _relationshipTypeFactory = new RelationshipTypeFactory(resourceFieldValidationMetadataProvider); _schemasForResourceFields = GetFieldSchemas(); } @@ -76,7 +78,7 @@ public void SetMembersOfAttributesObject(OpenApiSchema fullSchemaForAttributesOb { AddAttributeSchemaToResourceObject(matchingAttribute, fullSchemaForAttributesObject, resourceFieldSchema); - resourceFieldSchema.Nullable = matchingAttribute.IsNullable(); + resourceFieldSchema.Nullable = _resourceFieldValidationMetadataProvider.IsNullable(matchingAttribute); if (IsFieldRequired(matchingAttribute)) { @@ -112,29 +114,9 @@ private void ExposeSchema(OpenApiReference openApiReference, Type typeRepresente private bool IsFieldRequired(ResourceFieldAttribute field) { - if (_resourceTypeInfo.ResourceObjectOpenType != typeof(ResourceObjectInPostRequest<>)) - { - return false; - } - - if (field.Property.HasAttribute()) - { - return true; - } + bool isSchemaForUpdateResourceEndpoint = _resourceTypeInfo.ResourceObjectOpenType == typeof(ResourceObjectInPatchRequest<>); - if (field is HasManyAttribute) - { - return false; - } - - NullabilityInfoContext nullabilityContext = new(); - NullabilityInfo nullabilityInfo = nullabilityContext.Create(field.Property); - - return field.Property.PropertyType.IsValueType switch - { - true => false, - false => _options.ValidateModelState && nullabilityInfo.ReadState == NullabilityState.NotNull - }; + return !isSchemaForUpdateResourceEndpoint && _resourceFieldValidationMetadataProvider.IsRequired(field); } public void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelationshipsObject) @@ -192,11 +174,11 @@ private void AddRelationshipSchemaToResourceObject(RelationshipAttribute relatio } } - private static Type GetRelationshipSchemaType(RelationshipAttribute relationship, Type resourceObjectType) + private Type GetRelationshipSchemaType(RelationshipAttribute relationship, Type resourceObjectType) { return resourceObjectType.GetGenericTypeDefinition().IsAssignableTo(typeof(ResourceObjectInResponse<>)) - ? RelationshipTypeFactory.Instance.GetForResponse(relationship) - : RelationshipTypeFactory.Instance.GetForRequest(relationship); + ? _relationshipTypeFactory.GetForResponse(relationship) + : _relationshipTypeFactory.GetForRequest(relationship); } private OpenApiSchema? GetReferenceSchemaForRelationship(Type relationshipSchemaType) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index f7db9e86ef..8e061fff57 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -15,7 +15,7 @@ internal sealed class ResourceObjectSchemaGenerator private readonly Func _resourceFieldObjectSchemaBuilderFactory; public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options, - ISchemaRepositoryAccessor schemaRepositoryAccessor) + ISchemaRepositoryAccessor schemaRepositoryAccessor, ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(defaultSchemaGenerator); ArgumentGuard.NotNull(resourceGraph); @@ -31,7 +31,7 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe _allowClientGeneratedIds = options.AllowClientGeneratedIds; _resourceFieldObjectSchemaBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, - defaultSchemaGenerator, _resourceTypeSchemaGenerator, options); + defaultSchemaGenerator, _resourceTypeSchemaGenerator, options, resourceFieldValidationMetadataProvider); } public OpenApiSchema GenerateSchema(Type resourceObjectType) diff --git a/test/OpenApiClientTests/FakerFactory.cs b/test/OpenApiClientTests/FakerFactory.cs new file mode 100644 index 0000000000..c28adbaa60 --- /dev/null +++ b/test/OpenApiClientTests/FakerFactory.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using AutoBogus; + +namespace OpenApiClientTests; + +internal sealed class FakerFactory +{ + public static FakerFactory Instance { get; } = new(); + + private FakerFactory() + { + } + + public AutoFaker Create() + where TTarget : class + { + return new AutoFaker(); + } + + public AutoFaker CreateForObjectWithResourceId() + where TTarget : class + { + return new AutoFaker().Configure(builder => builder.WithOverride(new ResourceStringIdOverride())); + } + + private sealed class ResourceStringIdOverride : AutoGeneratorOverride + { + private readonly IAutoFaker _idFaker = AutoFaker.Create(); + + public override bool CanOverride(AutoGenerateContext context) + { + PropertyInfo? resourceIdPropertyInfo = context.GenerateType.GetProperty("Id"); + return resourceIdPropertyInfo != null && resourceIdPropertyInfo.PropertyType == typeof(string); + } + + public override void Generate(AutoGenerateOverrideContext context) + { + ((dynamic)context.Instance).Id = _idFaker.Generate()!.ToString()!; + } + } +} diff --git a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs index 55e882fedf..b6855c06d2 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs @@ -27,7 +27,7 @@ public async Task Disposed_request_document_registration_does_not_affect_request } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); @@ -72,7 +72,7 @@ public async Task Request_document_registration_can_be_used_for_multiple_request } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); @@ -128,10 +128,10 @@ public async Task Request_is_unaffected_by_request_document_registration_of_diff } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, + using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.AirtimeInHours)) { - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, + using (apiClient.WithPartialAttributeSerialization(requestDocument2, airplane => airplane.SerialNumber)) { } @@ -174,7 +174,7 @@ public async Task Attribute_values_can_be_changed_after_request_document_registr } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.IsInMaintenance)) { requestDocument.Data.Attributes.IsInMaintenance = false; @@ -223,10 +223,10 @@ public async Task Request_document_registration_is_unaffected_by_successive_regi } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, + using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.IsInMaintenance)) { - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, + using (apiClient.WithPartialAttributeSerialization(requestDocument2, airplane => airplane.AirtimeInHours)) { // Act @@ -265,7 +265,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, + using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, requestDocument1)); @@ -288,7 +288,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo wrapper.ChangeResponse(HttpStatusCode.NoContent, null); - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, + using (apiClient.WithPartialAttributeSerialization(requestDocument2, airplane => airplane.SerialNumber)) { // Act @@ -327,7 +327,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, + using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.AirtimeInHours)) { _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(requestDocument1)); @@ -350,7 +350,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo wrapper.ChangeResponse(HttpStatusCode.NoContent, null); - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, + using (apiClient.WithPartialAttributeSerialization(requestDocument2, airplane => airplane.SerialNumber)) { // Act @@ -401,10 +401,10 @@ public async Task Request_document_registration_is_unaffected_by_preceding_regis } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument1, + using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.SerialNumber)) { - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument2, + using (apiClient.WithPartialAttributeSerialization(requestDocument2, airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act diff --git a/test/OpenApiClientTests/LegacyClient/RequestTests.cs b/test/OpenApiClientTests/LegacyClient/RequestTests.cs index 8bb0c3f350..ab58b101e6 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestTests.cs @@ -152,7 +152,7 @@ public async Task Partial_posting_resource_produces_expected_request() } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.SerialNumber)) { // Act @@ -203,7 +203,7 @@ public async Task Partial_patching_resource_produces_expected_request() } }; - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, + using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.SerialNumber, airplane => airplane.LastServicedAt, airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act diff --git a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs index 10f86adca5..b112a23d05 100644 --- a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs @@ -149,7 +149,9 @@ public async Task Getting_resource_translates_response() // Arrange const string flightId = "ZvuH1"; const string departsAtInZuluTime = "2021-06-08T12:53:30.554Z"; + const string flightDestination = "Amsterdam"; const string arrivesAtWithUtcOffset = "2019-02-20T11:56:33.0721266+01:00"; + const string flightServiceOnBoard = "Movies"; const string responseBody = @"{ ""links"": { @@ -160,7 +162,9 @@ public async Task Getting_resource_translates_response() ""id"": """ + flightId + @""", ""attributes"": { ""departs-at"": """ + departsAtInZuluTime + @""", - ""arrives-at"": """ + arrivesAtWithUtcOffset + @""" + ""arrives-at"": """ + arrivesAtWithUtcOffset + @""", + ""final-destination"": """ + flightDestination + @""", + ""services-on-board"": [""" + flightServiceOnBoard + @"""] }, ""links"": { ""self"": """ + HostPrefix + @"flights/" + flightId + @""" @@ -181,8 +185,8 @@ public async Task Getting_resource_translates_response() document.Data.Relationships.Should().BeNull(); document.Data.Attributes.DepartsAt.Should().Be(DateTimeOffset.Parse(departsAtInZuluTime)); document.Data.Attributes.ArrivesAt.Should().Be(DateTimeOffset.Parse(arrivesAtWithUtcOffset)); - document.Data.Attributes.ServicesOnBoard.Should().BeNull(); - document.Data.Attributes.FinalDestination.Should().BeNull(); + document.Data.Attributes.ServicesOnBoard.Should().Contain(flightServiceOnBoard); + document.Data.Attributes.FinalDestination.Should().Be(flightDestination); document.Data.Attributes.StopOverDestination.Should().BeNull(); document.Data.Attributes.OperatedBy.Should().Be(default); } diff --git a/test/OpenApiClientTests/LegacyClient/swagger.g.json b/test/OpenApiClientTests/LegacyClient/swagger.g.json index f95214e8d4..043f6394eb 100644 --- a/test/OpenApiClientTests/LegacyClient/swagger.g.json +++ b/test/OpenApiClientTests/LegacyClient/swagger.g.json @@ -1970,6 +1970,9 @@ "additionalProperties": false }, "airplane-attributes-in-response": { + "required": [ + "name" + ], "type": "object", "properties": { "name": { @@ -2238,6 +2241,10 @@ "additionalProperties": false }, "flight-attendant-attributes-in-response": { + "required": [ + "email-address", + "profile-image-url" + ], "type": "object", "properties": { "email-address": { @@ -2557,6 +2564,10 @@ "additionalProperties": false }, "flight-attributes-in-response": { + "required": [ + "final-destination", + "services-on-board" + ], "type": "object", "properties": { "final-destination": { @@ -2815,6 +2826,9 @@ "additionalProperties": false }, "flight-relationships-in-response": { + "required": [ + "purser" + ], "type": "object", "properties": { "cabin-crew-members": { diff --git a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json index d2850c032d..5d2569050d 100644 --- a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json @@ -1001,6 +1001,9 @@ "additionalProperties": false }, "staffMemberAttributesInResponse": { + "required": [ + "name" + ], "type": "object", "properties": { "name": { @@ -1190,6 +1193,9 @@ "additionalProperties": false }, "supermarketAttributesInResponse": { + "required": [ + "nameOfCity" + ], "type": "object", "properties": { "nameOfCity": { @@ -1380,6 +1386,9 @@ "additionalProperties": false }, "supermarketRelationshipsInResponse": { + "required": [ + "storeManager" + ], "type": "object", "properties": { "storeManager": { diff --git a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json index 1d7feb51f0..9d7656c612 100644 --- a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json @@ -1001,6 +1001,9 @@ "additionalProperties": false }, "staff-member-attributes-in-response": { + "required": [ + "name" + ], "type": "object", "properties": { "name": { @@ -1190,6 +1193,9 @@ "additionalProperties": false }, "supermarket-attributes-in-response": { + "required": [ + "name-of-city" + ], "type": "object", "properties": { "name-of-city": { @@ -1380,6 +1386,9 @@ "additionalProperties": false }, "supermarket-relationships-in-response": { + "required": [ + "store-manager" + ], "type": "object", "properties": { "store-manager": { diff --git a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json index fa5041afad..ae79e8470b 100644 --- a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json @@ -1001,6 +1001,9 @@ "additionalProperties": false }, "StaffMemberAttributesInResponse": { + "required": [ + "Name" + ], "type": "object", "properties": { "Name": { @@ -1190,6 +1193,9 @@ "additionalProperties": false }, "SupermarketAttributesInResponse": { + "required": [ + "NameOfCity" + ], "type": "object", "properties": { "NameOfCity": { @@ -1380,6 +1386,9 @@ "additionalProperties": false }, "SupermarketRelationshipsInResponse": { + "required": [ + "StoreManager" + ], "type": "object", "properties": { "StoreManager": { diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs new file mode 100644 index 0000000000..cdf729c09b --- /dev/null +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using JsonApiDotNetCore.OpenApi.Client; + +namespace OpenApiClientTests; + +internal static class ObjectExtensions +{ + public static object? GetPropertyValue(this object source, string propertyName) + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + + return propertyInfo.GetValue(source); + } + + public static void SetPropertyValue(this object source, string propertyName, object? value) + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + + propertyInfo.SetValue(source, value); + } + + public static object? GetDefaultValueForProperty(this object source, string propertyName) + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + + return Activator.CreateInstance(propertyInfo.PropertyType); + } +} diff --git a/test/OpenApiClientTests/OpenApiClientTests.cs b/test/OpenApiClientTests/OpenApiClientTests.cs new file mode 100644 index 0000000000..bb0b5d61a1 --- /dev/null +++ b/test/OpenApiClientTests/OpenApiClientTests.cs @@ -0,0 +1,20 @@ +using System.Linq.Expressions; + +namespace OpenApiClientTests; + +public class OpenApiClientTests +{ + private const string AttributesObjectParameterName = "attributesObject"; + + protected static Expression> CreateIncludedAttributeSelector(string propertyName) + where TAttributesObject : class + { + Type attributesObjectType = typeof(TAttributesObject); + + ParameterExpression parameter = Expression.Parameter(attributesObjectType, AttributesObjectParameterName); + MemberExpression property = Expression.Property(parameter, propertyName); + UnaryExpression toObjectConversion = Expression.Convert(property, typeof(object)); + + return Expression.Lambda>(toObjectConversion, parameter); + } +} diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 4e726ad900..c5e7f0acdd 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -9,6 +9,7 @@ + @@ -52,19 +53,34 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions - - OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode - NullableReferenceTypesEnabledClient - NullableReferenceTypesEnabledClient.cs + + NrtOffMsvOffClient + NrtOffMsvOffClient.cs NSwagCSharp - /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - - OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode - NullableReferenceTypesDisabledClient - NullableReferenceTypesDisabledClient.cs + + NrtOffMsvOnClient + NrtOffMsvOnClient.cs NSwagCSharp + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false + + NrtOnMsvOffClient + NrtOnMsvOffClient.cs + NSwagCSharp + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + + + NrtOnMsvOnClient + NrtOnMsvOnClient.cs + NSwagCSharp + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + - \ No newline at end of file + + diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs new file mode 100644 index 0000000000..8888d8f56d --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs @@ -0,0 +1,528 @@ +using System.Linq.Expressions; +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +public sealed class CreateResourceTests : OpenApiClientTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne", Skip = "Known limitation")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] + public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne", Skip = "Known limitation")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] + public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs new file mode 100644 index 0000000000..4bea9db090 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; + +internal partial class NrtOffMsvOffClient : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..8079c4896c --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs new file mode 100644 index 0000000000..1bc729cbd9 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs @@ -0,0 +1,25 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +internal sealed class ResourceFieldValidationFakers +{ + private readonly Lazy> _lazyPostAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyPatchAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + public Faker PostAttributes => _lazyPostAttributesFaker.Value; + public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs new file mode 100644 index 0000000000..68e71e97b9 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs @@ -0,0 +1,146 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +public sealed class UpdateResourceTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Fact] + public async Task Cannot_exclude_id() + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceDataInPatchRequest emptyDataObject = new(); + requestDocument.Data.Id = emptyDataObject.Id; + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPatchRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredReferenceType), "requiredReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPatchRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json new file mode 100644 index 0000000000..572f1c7615 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json @@ -0,0 +1,1677 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/Resource": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourceCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResource", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/requiredToMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/requiredToMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/requiredToOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/requiredToOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/toMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/toMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/toOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/toOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "components": { + "schemas": { + "emptyResourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "emptyResourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/emptyResourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "emptyResourceIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/emptyResourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "emptyResourceIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "emptyResourceResourceType": { + "enum": [ + "emptyResources" + ], + "type": "string" + }, + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullableEmptyResourceIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableEmptyResourceSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneEmptyResourceInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneEmptyResourceInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourceAttributesInPatchRequest": { + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceAttributesInPostRequest": { + "required": [ + "requiredNullableValueType", + "requiredReferenceType", + "requiredValueType" + ], + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceAttributesInResponse": { + "required": [ + "requiredNullableValueType", + "requiredReferenceType", + "requiredValueType" + ], + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/resourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "resourceDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourcePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourcePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPostRequest" + } + }, + "additionalProperties": false + }, + "resourcePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPostRequest": { + "required": [ + "requiredToMany", + "requiredToOne" + ], + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInResponse": { + "required": [ + "requiredToMany", + "requiredToOne" + ], + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "requiredToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + } + }, + "additionalProperties": false + }, + "resourceResourceType": { + "enum": [ + "Resource" + ], + "type": "string" + }, + "toManyEmptyResourceInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyEmptyResourceInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs new file mode 100644 index 0000000000..f3a5130486 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs @@ -0,0 +1,569 @@ +using System.Linq.Expressions; +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; + +public sealed class CreateResourceTests : OpenApiClientTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] + public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(clrPropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs similarity index 58% rename from test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs index 8c6b98a237..94bb5efc37 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/GeneratedCode/NullableReferenceTypesDisabledClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs @@ -1,9 +1,9 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; -internal partial class NullableReferenceTypesDisabledClient : JsonApiClient +internal partial class NrtOffMsvOnClient : JsonApiClient { partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..53252ee439 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), NullabilityState.Unknown)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs new file mode 100644 index 0000000000..2960441a6c --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs @@ -0,0 +1,29 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; + +internal sealed class ResourceFieldValidationFakers +{ + private readonly Lazy> _lazyPostAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyPatchAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + public Faker PostAttributes => _lazyPostAttributesFaker.Value; + public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs new file mode 100644 index 0000000000..81f54f6dd0 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs @@ -0,0 +1,146 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; + +public sealed class UpdateResourceTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Fact] + public async Task Cannot_exclude_id() + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceDataInPatchRequest emptyDataObject = new(); + requestDocument.Data.Id = emptyDataObject.Id; + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPatchRequest.ReferenceType), "referenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredReferenceType), "requiredReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPatchRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + ToOne = _fakers.NullableToOne.Generate(), + RequiredToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json new file mode 100644 index 0000000000..f366cc19e2 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json @@ -0,0 +1,1744 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/Resource": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourceCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResource", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResource", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/requiredToMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/requiredToMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResourceRequiredToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/requiredToOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/requiredToOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceRequiredToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/toMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToMany", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/toMany": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Resource" + ], + "operationId": "postResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/toOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Resource/{id}/relationships/toOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Resource" + ], + "operationId": "headResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "components": { + "schemas": { + "emptyResourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "emptyResourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/emptyResourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "emptyResourceIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/emptyResourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "emptyResourceIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "emptyResourceIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "emptyResourceResourceType": { + "enum": [ + "emptyResources" + ], + "type": "string" + }, + "emptyResourceSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/emptyResourceDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "first": { + "minLength": 1, + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullableEmptyResourceIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableEmptyResourceSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneEmptyResourceInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneEmptyResourceInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourceAttributesInPatchRequest": { + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceAttributesInPostRequest": { + "required": [ + "requiredNullableValueType", + "requiredReferenceType" + ], + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceAttributesInResponse": { + "required": [ + "requiredNullableValueType", + "requiredReferenceType" + ], + "type": "object", + "properties": { + "referenceType": { + "type": "string", + "nullable": true + }, + "requiredReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/resourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "resourceDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourcePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourcePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPostRequest" + } + }, + "additionalProperties": false + }, + "resourcePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPostRequest": { + "required": [ + "requiredToOne" + ], + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInResponse": { + "required": [ + "requiredToOne" + ], + "type": "object", + "properties": { + "toOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "requiredToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + } + }, + "additionalProperties": false + }, + "resourceResourceType": { + "enum": [ + "Resource" + ], + "type": "string" + }, + "toManyEmptyResourceInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyEmptyResourceInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "toOneEmptyResourceInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + } + }, + "additionalProperties": false + }, + "toOneEmptyResourceInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/emptyResourceIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs new file mode 100644 index 0000000000..27e9c06b5c --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs @@ -0,0 +1,613 @@ +using System.Linq.Expressions; +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +public sealed class CreateResourceTests : OpenApiClientTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(clrPropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), + "requiredNullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), + "requiredNullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), + "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), + "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), + "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), + "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne", Skip = "Known limitation")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] + public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne", Skip = "Known limitation")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] + public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs new file mode 100644 index 0000000000..c44cdbe3d7 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; + +internal partial class NrtOnMsvOffClient : JsonApiClient +{ + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..dc2bc84ccd --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs new file mode 100644 index 0000000000..1c31f040b8 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs @@ -0,0 +1,29 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +internal sealed class ResourceFieldValidationFakers +{ + private readonly Lazy> _lazyPostAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyPatchAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + public Faker PostAttributes => _lazyPostAttributesFaker.Value; + public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs new file mode 100644 index 0000000000..5596c17962 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs @@ -0,0 +1,156 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +public sealed class UpdateResourceTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Fact] + public async Task Cannot_exclude_id() + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceDataInPatchRequest emptyDataObject = new(); + requestDocument.Data.Id = emptyDataObject.Id; + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPatchRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_exclude_attribute_that_is_required_in_create_resource(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPatchRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship_that_is_required_in_create_resource(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.NullableToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json similarity index 67% rename from test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json index 1f2d189d46..8619a357c8 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json @@ -5,19 +5,19 @@ "version": "1.0" }, "paths": { - "/cows": { + "/Resource": { "get": { "tags": [ - "cows" + "Resource" ], - "operationId": "getCowCollection", + "operationId": "getResourceCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/resourceCollectionResponseDocument" } } } @@ -26,16 +26,16 @@ }, "head": { "tags": [ - "cows" + "Resource" ], - "operationId": "headCowCollection", + "operationId": "headResourceCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/resourceCollectionResponseDocument" } } } @@ -44,14 +44,14 @@ }, "post": { "tags": [ - "cows" + "Resource" ], - "operationId": "postCow", + "operationId": "postResource", "requestBody": { "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPostRequestDocument" + "$ref": "#/components/schemas/resourcePostRequestDocument" } } } @@ -62,7 +62,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -73,12 +73,12 @@ } } }, - "/cows/{id}": { + "/Resource/{id}": { "get": { "tags": [ - "cows" + "Resource" ], - "operationId": "getCow", + "operationId": "getResource", "parameters": [ { "name": "id", @@ -96,7 +96,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -105,9 +105,9 @@ }, "head": { "tags": [ - "cows" + "Resource" ], - "operationId": "headCow", + "operationId": "headResource", "parameters": [ { "name": "id", @@ -125,7 +125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -134,9 +134,9 @@ }, "patch": { "tags": [ - "cows" + "Resource" ], - "operationId": "patchCow", + "operationId": "patchResource", "parameters": [ { "name": "id", @@ -152,7 +152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPatchRequestDocument" + "$ref": "#/components/schemas/resourcePatchRequestDocument" } } } @@ -163,7 +163,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -175,9 +175,9 @@ }, "delete": { "tags": [ - "cows" + "Resource" ], - "operationId": "deleteCow", + "operationId": "deleteResource", "parameters": [ { "name": "id", @@ -196,80 +196,72 @@ } } }, - "/cowStables": { + "/Resource/{id}/nonNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowStableCollectionResponseDocument" - } - } + "operationId": "getResourceNonNullableToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" } } - } - }, - "head": { - "tags": [ - "cowStables" ], - "operationId": "headCowStableCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowStableCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } } } }, - "post": { + "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "postCowStable", - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowStablePostRequestDocument" - } + "operationId": "headResourceNonNullableToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" } } - }, + ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } - }, - "204": { - "description": "No Content" } } } }, - "/cowStables/{id}": { + "/Resource/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStable", + "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -287,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -296,9 +288,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStable", + "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -316,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -325,9 +317,9 @@ }, "patch": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "patchCowStable", + "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -343,43 +335,11 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowStablePatchRequestDocument" + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" } } } }, - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowStablePrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "cowStables" - ], - "operationId": "deleteCowStable", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], "responses": { "204": { "description": "No Content" @@ -387,12 +347,12 @@ } } }, - "/cowStables/{id}/albinoCow": { + "/Resource/{id}/nullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableAlbinoCow", + "operationId": "getResourceNullableToOne", "parameters": [ { "name": "id", @@ -410,7 +370,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableCowSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" } } } @@ -419,9 +379,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableAlbinoCow", + "operationId": "headResourceNullableToOne", "parameters": [ { "name": "id", @@ -439,7 +399,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableCowSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" } } } @@ -447,12 +407,12 @@ } } }, - "/cowStables/{id}/relationships/albinoCow": { + "/Resource/{id}/relationships/nullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableAlbinoCowRelationship", + "operationId": "getResourceNullableToOneRelationship", "parameters": [ { "name": "id", @@ -470,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableCowIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } @@ -479,9 +439,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableAlbinoCowRelationship", + "operationId": "headResourceNullableToOneRelationship", "parameters": [ { "name": "id", @@ -499,7 +459,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableCowIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } @@ -508,9 +468,9 @@ }, "patch": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "patchCowStableAlbinoCowRelationship", + "operationId": "patchResourceNullableToOneRelationship", "parameters": [ { "name": "id", @@ -526,7 +486,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneCowInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" } } } @@ -538,12 +498,12 @@ } } }, - "/cowStables/{id}/allCows": { + "/Resource/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableAllCows", + "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ { "name": "id", @@ -561,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } @@ -570,9 +530,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableAllCows", + "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ { "name": "id", @@ -590,7 +550,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } @@ -598,12 +558,12 @@ } } }, - "/cowStables/{id}/relationships/allCows": { + "/Resource/{id}/relationships/requiredNonNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableAllCowsRelationship", + "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -621,7 +581,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -630,9 +590,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableAllCowsRelationship", + "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -650,80 +610,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } } } }, - "post": { - "tags": [ - "cowStables" - ], - "operationId": "postCowStableAllCowsRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, "patch": { "tags": [ - "cowStables" - ], - "operationId": "patchCowStableAllCowsRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" - } - } - }, - "delete": { - "tags": [ - "cowStables" + "Resource" ], - "operationId": "deleteCowStableAllCowsRelationship", + "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -739,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" } } } @@ -751,12 +649,12 @@ } } }, - "/cowStables/{id}/cowsReadyForMilking": { + "/Resource/{id}/requiredNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableCowsReadyForMilking", + "operationId": "getResourceRequiredNullableToOne", "parameters": [ { "name": "id", @@ -774,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" } } } @@ -783,9 +681,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableCowsReadyForMilking", + "operationId": "headResourceRequiredNullableToOne", "parameters": [ { "name": "id", @@ -803,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" } } } @@ -811,12 +709,12 @@ } } }, - "/cowStables/{id}/relationships/cowsReadyForMilking": { + "/Resource/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableCowsReadyForMilkingRelationship", + "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -834,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } @@ -843,9 +741,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableCowsReadyForMilkingRelationship", + "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -863,18 +761,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } } } }, - "post": { + "patch": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "postCowStableCowsReadyForMilkingRelationship", + "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -890,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" } } } @@ -900,12 +798,14 @@ "description": "No Content" } } - }, - "patch": { + } + }, + "/Resource/{id}/requiredToMany": { + "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "patchCowStableCowsReadyForMilkingRelationship", + "operationId": "getResourceRequiredToMany", "parameters": [ { "name": "id", @@ -917,26 +817,24 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } }, - "delete": { + "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "deleteCowStableCowsReadyForMilkingRelationship", + "operationId": "headResourceRequiredToMany", "parameters": [ { "name": "id", @@ -948,28 +846,26 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyCowInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } } }, - "/cowStables/{id}/favoriteCow": { + "/Resource/{id}/relationships/requiredToMany": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableFavoriteCow", + "operationId": "getResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -987,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } @@ -996,9 +892,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableFavoriteCow", + "operationId": "headResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -1016,20 +912,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } } } - } - }, - "/cowStables/{id}/relationships/favoriteCow": { - "get": { + }, + "post": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableFavoriteCowRelationship", + "operationId": "postResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -1041,24 +935,26 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } + }, + "responses": { + "204": { + "description": "No Content" + } } }, - "head": { + "patch": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableFavoriteCowRelationship", + "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -1070,24 +966,26 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } + }, + "responses": { + "204": { + "description": "No Content" + } } }, - "patch": { + "delete": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "patchCowStableFavoriteCowRelationship", + "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -1103,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneCowInRequest" + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } @@ -1115,12 +1013,12 @@ } } }, - "/cowStables/{id}/firstCow": { + "/Resource/{id}/toMany": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableFirstCow", + "operationId": "getResourceToMany", "parameters": [ { "name": "id", @@ -1138,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" } } } @@ -1147,9 +1045,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableFirstCow", + "operationId": "headResourceToMany", "parameters": [ { "name": "id", @@ -1167,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" } } } @@ -1175,12 +1073,12 @@ } } }, - "/cowStables/{id}/relationships/firstCow": { + "/Resource/{id}/relationships/toMany": { "get": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableFirstCowRelationship", + "operationId": "getResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1198,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } @@ -1207,9 +1105,9 @@ }, "head": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableFirstCowRelationship", + "operationId": "headResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1227,18 +1125,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } } } }, - "patch": { + "post": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "patchCowStableFirstCowRelationship", + "operationId": "postResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1254,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneCowInRequest" + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } @@ -1264,14 +1162,12 @@ "description": "No Content" } } - } - }, - "/cowStables/{id}/oldestCow": { - "get": { + }, + "patch": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "getCowStableOldestCow", + "operationId": "patchResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1283,24 +1179,26 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } + }, + "responses": { + "204": { + "description": "No Content" + } } }, - "head": { + "delete": { "tags": [ - "cowStables" + "Resource" ], - "operationId": "headCowStableOldestCow", + "operationId": "deleteResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1312,107 +1210,18 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowSecondaryResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } - } - } - } - }, - "/cowStables/{id}/relationships/oldestCow": { - "get": { - "tags": [ - "cowStables" - ], - "operationId": "getCowStableOldestCowRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "cowStables" - ], - "operationId": "headCowStableOldestCowRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/cowIdentifierResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "cowStables" - ], - "operationId": "patchCowStableOldestCowRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toOneCowInRequest" - } - } - } - }, - "responses": { - "204": { - "description": "No Content" + } + }, + "responses": { + "204": { + "description": "No Content" } } } @@ -1420,125 +1229,7 @@ }, "components": { "schemas": { - "cowAttributesInPatchRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "nameOfPreviousFarm": { - "type": "string", - "nullable": true - }, - "nickname": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedMilk": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "cowAttributesInPostRequest": { - "required": [ - "hasProducedMilk", - "name", - "nameOfCurrentFarm", - "nickname", - "weight" - ], - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "nameOfPreviousFarm": { - "type": "string", - "nullable": true - }, - "nickname": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedMilk": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "cowAttributesInResponse": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "nameOfPreviousFarm": { - "type": "string", - "nullable": true - }, - "nickname": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedMilk": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "cowCollectionResponseDocument": { + "emptyResourceCollectionResponseDocument": { "required": [ "data", "links" @@ -1548,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/cowDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" } }, "meta": { @@ -1564,42 +1255,7 @@ }, "additionalProperties": false }, - "cowDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/cowResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInPatchRequest" - } - }, - "additionalProperties": false - }, - "cowDataInPostRequest": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/cowResourceType" - }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInPostRequest" - } - }, - "additionalProperties": false - }, - "cowDataInResponse": { + "emptyResourceDataInResponse": { "required": [ "id", "links", @@ -1608,15 +1264,12 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/cowResourceType" + "$ref": "#/components/schemas/emptyResourceResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/cowAttributesInResponse" - }, "links": { "$ref": "#/components/schemas/linksInResourceObject" }, @@ -1627,7 +1280,7 @@ }, "additionalProperties": false }, - "cowIdentifier": { + "emptyResourceIdentifier": { "required": [ "id", "type" @@ -1635,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/cowResourceType" + "$ref": "#/components/schemas/emptyResourceResourceType" }, "id": { "minLength": 1, @@ -1644,7 +1297,7 @@ }, "additionalProperties": false }, - "cowIdentifierCollectionResponseDocument": { + "emptyResourceIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1654,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "meta": { @@ -1670,7 +1323,7 @@ }, "additionalProperties": false }, - "cowIdentifierResponseDocument": { + "emptyResourceIdentifierResponseDocument": { "required": [ "data", "links" @@ -1678,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, "meta": { "type": "object", @@ -1693,60 +1346,13 @@ }, "additionalProperties": false }, - "cowPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowDataInPatchRequest" - } - }, - "additionalProperties": false - }, - "cowPostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowDataInPostRequest" - } - }, - "additionalProperties": false - }, - "cowPrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "cowResourceType": { + "emptyResourceResourceType": { "enum": [ - "cows" + "emptyResources" ], "type": "string" }, - "cowSecondaryResponseDocument": { + "emptyResourceSecondaryResponseDocument": { "required": [ "data", "links" @@ -1754,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" }, "meta": { "type": "object", @@ -1769,252 +1375,32 @@ }, "additionalProperties": false }, - "cowStableCollectionResponseDocument": { - "required": [ - "data", - "links" - ], + "jsonapiObject": { "type": "object", "properties": { - "data": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { "type": "array", "items": { - "$ref": "#/components/schemas/cowStableDataInResponse" + "type": "string" } }, "meta": { "type": "object", "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" } }, "additionalProperties": false }, - "cowStableDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/cowStableResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/cowStableRelationshipsInPatchRequest" - } - }, - "additionalProperties": false - }, - "cowStableDataInPostRequest": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/cowStableResourceType" - }, - "relationships": { - "$ref": "#/components/schemas/cowStableRelationshipsInPostRequest" - } - }, - "additionalProperties": false - }, - "cowStableDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/cowStableResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "relationships": { - "$ref": "#/components/schemas/cowStableRelationshipsInResponse" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "cowStablePatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowStableDataInPatchRequest" - } - }, - "additionalProperties": false - }, - "cowStablePostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowStableDataInPostRequest" - } - }, - "additionalProperties": false - }, - "cowStablePrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/cowStableDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "cowStableRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "oldestCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "firstCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "albinoCow": { - "$ref": "#/components/schemas/nullableToOneCowInRequest" - }, - "favoriteCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "cowsReadyForMilking": { - "$ref": "#/components/schemas/toManyCowInRequest" - }, - "allCows": { - "$ref": "#/components/schemas/toManyCowInRequest" - } - }, - "additionalProperties": false - }, - "cowStableRelationshipsInPostRequest": { - "required": [ - "allCows", - "favoriteCow", - "firstCow", - "oldestCow" - ], - "type": "object", - "properties": { - "oldestCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "firstCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "albinoCow": { - "$ref": "#/components/schemas/nullableToOneCowInRequest" - }, - "favoriteCow": { - "$ref": "#/components/schemas/toOneCowInRequest" - }, - "cowsReadyForMilking": { - "$ref": "#/components/schemas/toManyCowInRequest" - }, - "allCows": { - "$ref": "#/components/schemas/toManyCowInRequest" - } - }, - "additionalProperties": false - }, - "cowStableRelationshipsInResponse": { - "type": "object", - "properties": { - "oldestCow": { - "$ref": "#/components/schemas/toOneCowInResponse" - }, - "firstCow": { - "$ref": "#/components/schemas/toOneCowInResponse" - }, - "albinoCow": { - "$ref": "#/components/schemas/nullableToOneCowInResponse" - }, - "favoriteCow": { - "$ref": "#/components/schemas/toOneCowInResponse" - }, - "cowsReadyForMilking": { - "$ref": "#/components/schemas/toManyCowInResponse" - }, - "allCows": { - "$ref": "#/components/schemas/toManyCowInResponse" - } - }, - "additionalProperties": false - }, - "cowStableResourceType": { - "enum": [ - "cowStables" - ], - "type": "string" - }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "linksInRelationshipObject": { + "linksInRelationshipObject": { "required": [ "related", "self" @@ -2170,7 +1556,7 @@ }, "nullable": true }, - "nullableCowIdentifierResponseDocument": { + "nullableEmptyResourceIdentifierResponseDocument": { "required": [ "data", "links" @@ -2180,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -2200,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableCowSecondaryResponseDocument": { + "nullableEmptyResourceSecondaryResponseDocument": { "required": [ "data", "links" @@ -2210,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/cowDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -2230,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneCowInRequest": { + "nullableToOneEmptyResourceInRequest": { "required": [ "data" ], @@ -2239,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -2249,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneCowInResponse": { + "nullableToOneEmptyResourceInResponse": { "required": [ "links" ], @@ -2258,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -2275,7 +1661,372 @@ }, "additionalProperties": false }, - "toManyCowInRequest": { + "resourceAttributesInPatchRequest": { + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceAttributesInPostRequest": { + "required": [ + "requiredNonNullableReferenceType", + "requiredNullableReferenceType", + "requiredNullableValueType", + "requiredValueType" + ], + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceAttributesInResponse": { + "required": [ + "requiredNonNullableReferenceType", + "requiredNullableReferenceType", + "requiredNullableValueType", + "requiredValueType" + ], + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string", + "nullable": true + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "resourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/resourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "resourceDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourcePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourcePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPostRequest" + } + }, + "additionalProperties": false + }, + "resourcePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPostRequest": { + "required": [ + "requiredNonNullableToOne", + "requiredNullableToOne", + "requiredToMany" + ], + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInResponse": { + "required": [ + "requiredNonNullableToOne", + "requiredNullableToOne", + "requiredToMany" + ], + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + } + }, + "additionalProperties": false + }, + "resourceResourceType": { + "enum": [ + "Resource" + ], + "type": "string" + }, + "toManyEmptyResourceInRequest": { "required": [ "data" ], @@ -2284,13 +2035,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } } }, "additionalProperties": false }, - "toManyCowInResponse": { + "toManyEmptyResourceInResponse": { "required": [ "links" ], @@ -2299,7 +2050,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "links": { @@ -2312,26 +2063,26 @@ }, "additionalProperties": false }, - "toOneCowInRequest": { + "toOneEmptyResourceInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "additionalProperties": false }, - "toOneCowInResponse": { + "toOneEmptyResourceInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/cowIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs new file mode 100644 index 0000000000..44aa5da920 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs @@ -0,0 +1,613 @@ +using System.Linq.Expressions; +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +public sealed class CreateResourceTests : OpenApiClientTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(attributePropertyName); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + Expression> includeAttributeSelector = + CreateIncludedAttributeSelector(clrPropertyName); + + using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPostRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + { + relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), + "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), + "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), + "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), + "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), + "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), + "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, + string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); + relationshipObject!.SetPropertyValue(dataPropertyName, null); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePostRequestDocument + { + Data = new ResourceDataInPostRequest + { + Attributes = _fakers.PostAttributes.Generate(), + Relationships = new ResourceRelationshipsInPostRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + Func> action = async () => + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs similarity index 59% rename from test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs index aa15aa3c91..1eed829acb 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/GeneratedCode/NullableReferenceTypesEnabledClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs @@ -1,9 +1,9 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; -internal partial class NullableReferenceTypesEnabledClient : JsonApiClient +internal partial class NrtOnMsvOnClient : JsonApiClient { partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..e3c1a5ce31 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using FluentAssertions; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +public sealed class NullabilityTests +{ + [Theory] + [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] + public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); + PropertyInfo property = properties.Single(property => property.Name == propertyName); + property.Should().HaveNullabilityState(expectedState); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs new file mode 100644 index 0000000000..8389b8bd2c --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs @@ -0,0 +1,29 @@ +using Bogus; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +internal sealed class ResourceFieldValidationFakers +{ + private readonly Lazy> _lazyPostAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyPatchAttributesFaker = new(() => + FakerFactory.Instance.Create()); + + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); + + public Faker PostAttributes => _lazyPostAttributesFaker.Value; + public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; +} diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs new file mode 100644 index 0000000000..d0695d6cf7 --- /dev/null +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs @@ -0,0 +1,156 @@ +using System.Net; +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Specialized; +using Newtonsoft.Json; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +public sealed class UpdateResourceTests +{ + private readonly ResourceFieldValidationFakers _fakers = new(); + + [Fact] + public async Task Cannot_exclude_id() + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceDataInPatchRequest emptyDataObject = new(); + requestDocument.Data.Id = emptyDataObject.Id; + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + // Act + Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + + // Assert + await action.Should().ThrowAsync(); + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + } + + [Theory] + [InlineData(nameof(ResourceAttributesInPatchRequest.NonNullableReferenceType), "nonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableReferenceType), "nullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.ValueType), "valueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceAttributesInPatchRequest emptyAttributesObject = new(); + object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.attributes").With(attributesObject => + { + attributesObject.ShouldNotContainPath(jsonPropertyName); + }); + } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] + public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + { + // Arrange + var requestDocument = new ResourcePatchRequestDocument + { + Data = new ResourceDataInPatchRequest + { + Id = "1", + Attributes = _fakers.PatchAttributes.Generate(), + Relationships = new ResourceRelationshipsInPatchRequest + { + NonNullableToOne = _fakers.ToOne.Generate(), + RequiredNonNullableToOne = _fakers.ToOne.Generate(), + NullableToOne = _fakers.NullableToOne.Generate(), + RequiredNullableToOne = _fakers.ToOne.Generate(), + ToMany = _fakers.ToMany.Generate(), + RequiredToMany = _fakers.ToMany.Generate() + } + } + }; + + ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); + requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + + using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); + var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + + using (apiClient.WithPartialAttributeSerialization(requestDocument)) + { + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + } + + // Assert + JsonElement document = wrapper.ParseRequestBody(); + + document.ShouldContainPath("data.relationships").With(relationshipsObject => + { + relationshipsObject.ShouldNotContainPath(jsonPropertyName); + }); + } +} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json similarity index 68% rename from test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json index 8357d61d05..897983660d 100644 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json @@ -5,19 +5,19 @@ "version": "1.0" }, "paths": { - "/chickens": { + "/Resource": { "get": { "tags": [ - "chickens" + "Resource" ], - "operationId": "getChickenCollection", + "operationId": "getResourceCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/resourceCollectionResponseDocument" } } } @@ -26,16 +26,16 @@ }, "head": { "tags": [ - "chickens" + "Resource" ], - "operationId": "headChickenCollection", + "operationId": "headResourceCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/resourceCollectionResponseDocument" } } } @@ -44,14 +44,14 @@ }, "post": { "tags": [ - "chickens" + "Resource" ], - "operationId": "postChicken", + "operationId": "postResource", "requestBody": { "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPostRequestDocument" + "$ref": "#/components/schemas/resourcePostRequestDocument" } } } @@ -62,7 +62,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -73,12 +73,12 @@ } } }, - "/chickens/{id}": { + "/Resource/{id}": { "get": { "tags": [ - "chickens" + "Resource" ], - "operationId": "getChicken", + "operationId": "getResource", "parameters": [ { "name": "id", @@ -96,7 +96,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -105,9 +105,9 @@ }, "head": { "tags": [ - "chickens" + "Resource" ], - "operationId": "headChicken", + "operationId": "headResource", "parameters": [ { "name": "id", @@ -125,7 +125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -134,9 +134,9 @@ }, "patch": { "tags": [ - "chickens" + "Resource" ], - "operationId": "patchChicken", + "operationId": "patchResource", "parameters": [ { "name": "id", @@ -152,7 +152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPatchRequestDocument" + "$ref": "#/components/schemas/resourcePatchRequestDocument" } } } @@ -163,7 +163,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenPrimaryResponseDocument" + "$ref": "#/components/schemas/resourcePrimaryResponseDocument" } } } @@ -175,9 +175,9 @@ }, "delete": { "tags": [ - "chickens" + "Resource" ], - "operationId": "deleteChicken", + "operationId": "deleteResource", "parameters": [ { "name": "id", @@ -196,80 +196,72 @@ } } }, - "/henHouses": { + "/Resource/{id}/nonNullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseCollection", - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/henHouseCollectionResponseDocument" - } - } + "operationId": "getResourceNonNullableToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" } } - } - }, - "head": { - "tags": [ - "henHouses" ], - "operationId": "headHenHouseCollection", "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHouseCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } } } }, - "post": { + "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "postHenHouse", - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/henHousePostRequestDocument" - } + "operationId": "headResourceNonNullableToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" } } - }, + ], "responses": { - "201": { - "description": "Created", + "200": { + "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } - }, - "204": { - "description": "No Content" } } } }, - "/henHouses/{id}": { + "/Resource/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouse", + "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -287,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -296,9 +288,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouse", + "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -316,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -325,9 +317,9 @@ }, "patch": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "patchHenHouse", + "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -343,32 +335,53 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHousePatchRequestDocument" + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" } } } }, + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/Resource/{id}/nullableToOne": { + "get": { + "tags": [ + "Resource" + ], + "operationId": "getResourceNullableToOne", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], "responses": { "200": { "description": "Success", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/henHousePrimaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" } } } - }, - "204": { - "description": "No Content" } } }, - "delete": { + "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "deleteHenHouse", + "operationId": "headResourceNullableToOne", "parameters": [ { "name": "id", @@ -381,18 +394,25 @@ } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + } + } + } } } } }, - "/henHouses/{id}/allChickens": { + "/Resource/{id}/relationships/nullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseAllChickens", + "operationId": "getResourceNullableToOneRelationship", "parameters": [ { "name": "id", @@ -410,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } @@ -419,9 +439,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseAllChickens", + "operationId": "headResourceNullableToOneRelationship", "parameters": [ { "name": "id", @@ -439,20 +459,51 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" } } } } } + }, + "patch": { + "tags": [ + "Resource" + ], + "operationId": "patchResourceNullableToOneRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + } + } } }, - "/henHouses/{id}/relationships/allChickens": { + "/Resource/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseAllChickensRelationship", + "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ { "name": "id", @@ -470,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } @@ -479,9 +530,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseAllChickensRelationship", + "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ { "name": "id", @@ -499,18 +550,20 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } } } - }, - "post": { + } + }, + "/Resource/{id}/relationships/requiredNonNullableToOne": { + "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "postHenHouseAllChickensRelationship", + "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -522,26 +575,24 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } }, - "patch": { + "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "patchHenHouseAllChickensRelationship", + "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -553,26 +604,24 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } }, - "delete": { + "patch": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "deleteHenHouseAllChickensRelationship", + "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ { "name": "id", @@ -588,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" } } } @@ -600,12 +649,12 @@ } } }, - "/henHouses/{id}/chickensReadyForLaying": { + "/Resource/{id}/requiredNullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseChickensReadyForLaying", + "operationId": "getResourceRequiredNullableToOne", "parameters": [ { "name": "id", @@ -623,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } @@ -632,9 +681,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseChickensReadyForLaying", + "operationId": "headResourceRequiredNullableToOne", "parameters": [ { "name": "id", @@ -652,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" } } } @@ -660,12 +709,12 @@ } } }, - "/henHouses/{id}/relationships/chickensReadyForLaying": { + "/Resource/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseChickensReadyForLayingRelationship", + "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -683,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } @@ -692,9 +741,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseChickensReadyForLayingRelationship", + "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -712,18 +761,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" } } } } } }, - "post": { + "patch": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "postHenHouseChickensReadyForLayingRelationship", + "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ { "name": "id", @@ -739,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" } } } @@ -749,12 +798,14 @@ "description": "No Content" } } - }, - "patch": { + } + }, + "/Resource/{id}/requiredToMany": { + "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "patchHenHouseChickensReadyForLayingRelationship", + "operationId": "getResourceRequiredToMany", "parameters": [ { "name": "id", @@ -766,26 +817,24 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } }, - "delete": { + "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "deleteHenHouseChickensReadyForLayingRelationship", + "operationId": "headResourceRequiredToMany", "parameters": [ { "name": "id", @@ -797,28 +846,26 @@ } } ], - "requestBody": { - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/toManyChickenInRequest" + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + } } } } - }, - "responses": { - "204": { - "description": "No Content" - } } } }, - "/henHouses/{id}/firstChicken": { + "/Resource/{id}/relationships/requiredToMany": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseFirstChicken", + "operationId": "getResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -836,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } @@ -845,9 +892,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseFirstChicken", + "operationId": "headResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -865,20 +912,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/chickenSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } } } - } - }, - "/henHouses/{id}/relationships/firstChicken": { - "get": { + }, + "post": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseFirstChickenRelationship", + "operationId": "postResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -890,24 +935,26 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/chickenIdentifierResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } + }, + "responses": { + "204": { + "description": "No Content" + } } }, - "head": { + "patch": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseFirstChickenRelationship", + "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -919,24 +966,26 @@ } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/chickenIdentifierResponseDocument" - } + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } + }, + "responses": { + "204": { + "description": "No Content" + } } }, - "patch": { + "delete": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "patchHenHouseFirstChickenRelationship", + "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ { "name": "id", @@ -952,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneChickenInRequest" + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } @@ -964,12 +1013,12 @@ } } }, - "/henHouses/{id}/oldestChicken": { + "/Resource/{id}/toMany": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseOldestChicken", + "operationId": "getResourceToMany", "parameters": [ { "name": "id", @@ -987,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableChickenSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" } } } @@ -996,9 +1045,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseOldestChicken", + "operationId": "headResourceToMany", "parameters": [ { "name": "id", @@ -1016,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableChickenSecondaryResponseDocument" + "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" } } } @@ -1024,12 +1073,12 @@ } } }, - "/henHouses/{id}/relationships/oldestChicken": { + "/Resource/{id}/relationships/toMany": { "get": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "getHenHouseOldestChickenRelationship", + "operationId": "getResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1047,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableChickenIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } @@ -1056,9 +1105,9 @@ }, "head": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "headHenHouseOldestChickenRelationship", + "operationId": "headResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1076,18 +1125,18 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableChickenIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" } } } } } }, - "patch": { + "post": { "tags": [ - "henHouses" + "Resource" ], - "operationId": "patchHenHouseOldestChickenRelationship", + "operationId": "postResourceToManyRelationship", "parameters": [ { "name": "id", @@ -1103,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneChickenInRequest" + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" } } } @@ -1113,107 +1162,74 @@ "description": "No Content" } } - } - } - }, - "components": { - "schemas": { - "chickenAttributesInPatchRequest": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedEggs": { - "type": "boolean" - } - }, - "additionalProperties": false }, - "chickenAttributesInPostRequest": { - "required": [ - "hasProducedEggs", - "nameOfCurrentFarm", - "weight" + "patch": { + "tags": [ + "Resource" ], - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedEggs": { - "type": "boolean" + "operationId": "patchResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } } }, - "additionalProperties": false + "responses": { + "204": { + "description": "No Content" + } + } }, - "chickenAttributesInResponse": { - "type": "object", - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "nameOfCurrentFarm": { - "minLength": 1, - "type": "string" - }, - "age": { - "type": "integer", - "format": "int32" - }, - "weight": { - "type": "integer", - "format": "int32" - }, - "timeAtCurrentFarmInDays": { - "type": "integer", - "format": "int32", - "nullable": true - }, - "hasProducedEggs": { - "type": "boolean" + "delete": { + "tags": [ + "Resource" + ], + "operationId": "deleteResourceToManyRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + } } }, - "additionalProperties": false - }, - "chickenCollectionResponseDocument": { + "responses": { + "204": { + "description": "No Content" + } + } + } + } + }, + "components": { + "schemas": { + "emptyResourceCollectionResponseDocument": { "required": [ "data", "links" @@ -1223,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/chickenDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" } }, "meta": { @@ -1239,42 +1255,7 @@ }, "additionalProperties": false }, - "chickenDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/chickenResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInPatchRequest" - } - }, - "additionalProperties": false - }, - "chickenDataInPostRequest": { - "required": [ - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/chickenResourceType" - }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInPostRequest" - } - }, - "additionalProperties": false - }, - "chickenDataInResponse": { + "emptyResourceDataInResponse": { "required": [ "id", "links", @@ -1283,15 +1264,12 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/chickenResourceType" + "$ref": "#/components/schemas/emptyResourceResourceType" }, "id": { "minLength": 1, "type": "string" }, - "attributes": { - "$ref": "#/components/schemas/chickenAttributesInResponse" - }, "links": { "$ref": "#/components/schemas/linksInResourceObject" }, @@ -1302,7 +1280,7 @@ }, "additionalProperties": false }, - "chickenIdentifier": { + "emptyResourceIdentifier": { "required": [ "id", "type" @@ -1310,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/chickenResourceType" + "$ref": "#/components/schemas/emptyResourceResourceType" }, "id": { "minLength": 1, @@ -1319,7 +1297,7 @@ }, "additionalProperties": false }, - "chickenIdentifierCollectionResponseDocument": { + "emptyResourceIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1329,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "meta": { @@ -1345,7 +1323,7 @@ }, "additionalProperties": false }, - "chickenIdentifierResponseDocument": { + "emptyResourceIdentifierResponseDocument": { "required": [ "data", "links" @@ -1353,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, "meta": { "type": "object", @@ -1368,60 +1346,13 @@ }, "additionalProperties": false }, - "chickenPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/chickenDataInPatchRequest" - } - }, - "additionalProperties": false - }, - "chickenPostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/chickenDataInPostRequest" - } - }, - "additionalProperties": false - }, - "chickenPrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/chickenDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "chickenResourceType": { + "emptyResourceResourceType": { "enum": [ - "chickens" + "emptyResources" ], "type": "string" }, - "chickenSecondaryResponseDocument": { + "emptyResourceSecondaryResponseDocument": { "required": [ "data", "links" @@ -1429,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" }, "meta": { "type": "object", @@ -1444,296 +1375,96 @@ }, "additionalProperties": false }, - "henHouseCollectionResponseDocument": { - "required": [ - "data", - "links" - ], + "jsonapiObject": { "type": "object", "properties": { - "data": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { "type": "array", "items": { - "$ref": "#/components/schemas/henHouseDataInResponse" + "type": "string" } }, "meta": { "type": "object", "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" } }, "additionalProperties": false }, - "henHouseDataInPatchRequest": { + "linksInRelationshipObject": { "required": [ - "id", - "type" + "related", + "self" ], "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/henHouseResourceType" - }, - "id": { + "self": { "minLength": 1, "type": "string" }, - "relationships": { - "$ref": "#/components/schemas/henHouseRelationshipsInPatchRequest" + "related": { + "minLength": 1, + "type": "string" } }, "additionalProperties": false }, - "henHouseDataInPostRequest": { + "linksInResourceCollectionDocument": { "required": [ - "type" + "first", + "self" ], "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/henHouseResourceType" + "self": { + "minLength": 1, + "type": "string" }, - "relationships": { - "$ref": "#/components/schemas/henHouseRelationshipsInPostRequest" - } - }, - "additionalProperties": false - }, - "henHouseDataInResponse": { - "required": [ - "id", - "links", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/henHouseResourceType" + "describedby": { + "type": "string" }, - "id": { + "first": { "minLength": 1, "type": "string" }, - "relationships": { - "$ref": "#/components/schemas/henHouseRelationshipsInResponse" + "last": { + "type": "string" }, - "links": { - "$ref": "#/components/schemas/linksInResourceObject" + "prev": { + "type": "string" }, - "meta": { - "type": "object", - "additionalProperties": { } + "next": { + "type": "string" } }, "additionalProperties": false }, - "henHousePatchRequestDocument": { + "linksInResourceDocument": { "required": [ - "data" + "self" ], "type": "object", "properties": { - "data": { - "$ref": "#/components/schemas/henHouseDataInPatchRequest" + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" } }, "additionalProperties": false }, - "henHousePostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/henHouseDataInPostRequest" - } - }, - "additionalProperties": false - }, - "henHousePrimaryResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "data": { - "$ref": "#/components/schemas/henHouseDataInResponse" - }, - "meta": { - "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" - } - }, - "additionalProperties": false - }, - "henHouseRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "oldestChicken": { - "$ref": "#/components/schemas/nullableToOneChickenInRequest" - }, - "firstChicken": { - "$ref": "#/components/schemas/toOneChickenInRequest" - }, - "allChickens": { - "$ref": "#/components/schemas/toManyChickenInRequest" - }, - "chickensReadyForLaying": { - "$ref": "#/components/schemas/toManyChickenInRequest" - } - }, - "additionalProperties": false - }, - "henHouseRelationshipsInPostRequest": { - "required": [ - "chickensReadyForLaying", - "firstChicken" - ], - "type": "object", - "properties": { - "oldestChicken": { - "$ref": "#/components/schemas/nullableToOneChickenInRequest" - }, - "firstChicken": { - "$ref": "#/components/schemas/toOneChickenInRequest" - }, - "allChickens": { - "$ref": "#/components/schemas/toManyChickenInRequest" - }, - "chickensReadyForLaying": { - "$ref": "#/components/schemas/toManyChickenInRequest" - } - }, - "additionalProperties": false - }, - "henHouseRelationshipsInResponse": { - "type": "object", - "properties": { - "oldestChicken": { - "$ref": "#/components/schemas/nullableToOneChickenInResponse" - }, - "firstChicken": { - "$ref": "#/components/schemas/toOneChickenInResponse" - }, - "allChickens": { - "$ref": "#/components/schemas/toManyChickenInResponse" - }, - "chickensReadyForLaying": { - "$ref": "#/components/schemas/toManyChickenInResponse" - } - }, - "additionalProperties": false - }, - "henHouseResourceType": { - "enum": [ - "henHouses" - ], - "type": "string" - }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "linksInRelationshipObject": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "related": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceCollectionDocument": { - "required": [ - "first", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "first": { - "minLength": 1, - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceDocument": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceIdentifierCollectionDocument": { + "linksInResourceIdentifierCollectionDocument": { "required": [ "first", "related", @@ -1825,7 +1556,7 @@ }, "nullable": true }, - "nullableChickenIdentifierResponseDocument": { + "nullableEmptyResourceIdentifierResponseDocument": { "required": [ "data", "links" @@ -1835,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1855,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableChickenSecondaryResponseDocument": { + "nullableEmptyResourceSecondaryResponseDocument": { "required": [ "data", "links" @@ -1865,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/chickenDataInResponse" + "$ref": "#/components/schemas/emptyResourceDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1885,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneChickenInRequest": { + "nullableToOneEmptyResourceInRequest": { "required": [ "data" ], @@ -1894,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1904,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneChickenInResponse": { + "nullableToOneEmptyResourceInResponse": { "required": [ "links" ], @@ -1913,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1930,7 +1661,366 @@ }, "additionalProperties": false }, - "toManyChickenInRequest": { + "resourceAttributesInPatchRequest": { + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceAttributesInPostRequest": { + "required": [ + "nonNullableReferenceType", + "requiredNonNullableReferenceType", + "requiredNullableReferenceType", + "requiredNullableValueType" + ], + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceAttributesInResponse": { + "required": [ + "nonNullableReferenceType", + "requiredNonNullableReferenceType", + "requiredNullableReferenceType", + "requiredNullableValueType" + ], + "type": "object", + "properties": { + "nonNullableReferenceType": { + "type": "string" + }, + "requiredNonNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "nullableReferenceType": { + "type": "string", + "nullable": true + }, + "requiredNullableReferenceType": { + "minLength": 1, + "type": "string" + }, + "valueType": { + "type": "integer", + "format": "int32" + }, + "requiredValueType": { + "type": "integer", + "format": "int32" + }, + "nullableValueType": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "requiredNullableValueType": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "resourceCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/resourceDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "resourceDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "resourceDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/resourceResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/resourceAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/resourceRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "resourcePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "resourcePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInPostRequest" + } + }, + "additionalProperties": false + }, + "resourcePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/resourceDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInPostRequest": { + "required": [ + "nonNullableToOne", + "requiredNonNullableToOne", + "requiredNullableToOne" + ], + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + } + }, + "additionalProperties": false + }, + "resourceRelationshipsInResponse": { + "required": [ + "nonNullableToOne", + "requiredNonNullableToOne", + "requiredNullableToOne" + ], + "type": "object", + "properties": { + "nonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "requiredNonNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "nullableToOne": { + "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + }, + "requiredNullableToOne": { + "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + }, + "toMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + }, + "requiredToMany": { + "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + } + }, + "additionalProperties": false + }, + "resourceResourceType": { + "enum": [ + "Resource" + ], + "type": "string" + }, + "toManyEmptyResourceInRequest": { "required": [ "data" ], @@ -1939,13 +2029,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } } }, "additionalProperties": false }, - "toManyChickenInResponse": { + "toManyEmptyResourceInResponse": { "required": [ "links" ], @@ -1954,7 +2044,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "links": { @@ -1967,26 +2057,26 @@ }, "additionalProperties": false }, - "toOneChickenInRequest": { + "toOneEmptyResourceInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" } }, "additionalProperties": false }, - "toOneChickenInResponse": { + "toOneEmptyResourceInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/chickenIdentifier" + "$ref": "#/components/schemas/emptyResourceIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs deleted file mode 100644 index a979e26901..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(ChickenAttributesInResponse.Name), NullabilityState.Unknown)] - [InlineData(nameof(ChickenAttributesInResponse.NameOfCurrentFarm), NullabilityState.Unknown)] - [InlineData(nameof(ChickenAttributesInResponse.Age), NullabilityState.NotNull)] - [InlineData(nameof(ChickenAttributesInResponse.Weight), NullabilityState.NotNull)] - [InlineData(nameof(ChickenAttributesInResponse.TimeAtCurrentFarmInDays), NullabilityState.Nullable)] - [InlineData(nameof(ChickenAttributesInResponse.HasProducedEggs), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(ChickenAttributesInResponse).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs deleted file mode 100644 index 1f63b62ff9..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledFaker.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Bogus; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -// @formatter:wrap_chained_method_calls chop_always -// @formatter:keep_existing_linebreaks true - -internal sealed class NullableReferenceTypesDisabledFaker -{ - private readonly Lazy> _lazyHenHousePostRequestDocumentFaker; - private readonly Lazy> _lazyHenHousePatchRequestDocumentFaker; - - private readonly Lazy> _lazyChickenPostRequestDocumentFaker = new(() => - { - Faker attributesInPostRequestFaker = new Faker() - .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) - .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) - .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) - .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) - .RuleFor(attributes => attributes.HasProducedEggs, _ => true); - - Faker dataInPostRequestFaker = new Faker() - .RuleFor(data => data.Attributes, _ => attributesInPostRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); - }); - - private readonly Lazy> _lazyChickenPatchRequestDocumentFaker = new(() => - { - Faker attributesInPatchRequestFaker = new Faker() - .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) - .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) - .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) - .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) - .RuleFor(attributes => attributes.HasProducedEggs, _ => true); - - Faker dataInPatchRequestFaker = new Faker() - // @formatter:wrap_chained_method_calls chop_if_long - .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) - // @formatter:wrap_chained_method_calls restore - .RuleFor(data => data.Attributes, _ => attributesInPatchRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); - }); - - private readonly Lazy> _lazyToOneChickenInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new ChickenIdentifier - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - })); - - private readonly Lazy> _lazyNullableToOneChickenInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new ChickenIdentifier - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - })); - - private readonly Lazy> _lazyToManyChickenInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new List - { - new() - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - } - })); - - public Faker ChickenPostRequestDocument => _lazyChickenPostRequestDocumentFaker.Value; - public Faker ChickenPatchRequestDocument => _lazyChickenPatchRequestDocumentFaker.Value; - public Faker HenHousePostRequestDocument => _lazyHenHousePostRequestDocumentFaker.Value; - public Faker HenHousePatchRequestDocument => _lazyHenHousePatchRequestDocumentFaker.Value; - - public NullableReferenceTypesDisabledFaker() - { - _lazyHenHousePostRequestDocumentFaker = new Lazy>(HenHousePostRequestDocumentFakerFactory); - _lazyHenHousePatchRequestDocumentFaker = new Lazy>(HenHousePatchRequestDocumentFakerFactory); - } - - private Faker HenHousePostRequestDocumentFakerFactory() - { - Faker relationshipsInPostRequestFaker = new Faker() - .RuleFor(relationships => relationships.OldestChicken, _ => _lazyNullableToOneChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FirstChicken, _ => _lazyToOneChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.ChickensReadyForLaying, _ => _lazyToManyChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AllChickens, _ => _lazyToManyChickenInRequestFaker.Value.Generate()); - - Faker dataInPostRequestFaker = new Faker() - .RuleFor(data => data.Relationships, _ => relationshipsInPostRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); - } - - private Faker HenHousePatchRequestDocumentFakerFactory() - { - Faker relationshipsInPatchRequestFaker = new Faker() - .RuleFor(relationships => relationships.OldestChicken, _ => _lazyNullableToOneChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FirstChicken, _ => _lazyToOneChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.ChickensReadyForLaying, _ => _lazyToManyChickenInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AllChickens, _ => _lazyToManyChickenInRequestFaker.Value.Generate()); - - Faker dataInPatchRequestFaker = new Faker() - // @formatter:wrap_chained_method_calls chop_if_long - .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) - // @formatter:wrap_chained_method_calls restore - .RuleFor(data => data.Relationships, _ => relationshipsInPatchRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs deleted file mode 100644 index 75d086e6ce..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/RequestTests.cs +++ /dev/null @@ -1,638 +0,0 @@ -using System.Net; -using System.Text.Json; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -public sealed class RequestTests -{ - private readonly NullableReferenceTypesDisabledFaker _fakers = new(); - - [Fact] - public async Task Can_clear_reference_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Name = null; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - chicken => chicken.Name)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("name").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Fact] - public async Task Can_exclude_reference_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Name = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("name"); - }); - } - - [Fact] - public async Task Cannot_clear_required_reference_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.NameOfCurrentFarm = default; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'nameOfCurrentFarm'. Property requires a value. Path 'data.attributes'."); - } - - [Fact] - public async Task Cannot_exclude_required_reference_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.NameOfCurrentFarm = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'nameOfCurrentFarm' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_set_default_value_to_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Age = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("age").With(attribute => attribute.ShouldBeInteger(0)); - }); - } - - [Fact] - public async Task Can_exclude_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Age = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("age"); - }); - } - - [Fact] - public async Task Can_set_default_value_to_required_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Weight = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("weight").With(attribute => attribute.ShouldBeInteger(0)); - }); - } - - [Fact] - public async Task Cannot_exclude_required_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Weight = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'weight' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_clear_nullable_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = null; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - chicken => chicken.TimeAtCurrentFarmInDays)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("timeAtCurrentFarmInDays").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Fact] - public async Task Can_exclude_nullable_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_nullable_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.HasProducedEggs = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'hasProducedEggs' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_set_default_value_to_required_nullable_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPostRequestDocument requestDocument = _fakers.ChickenPostRequestDocument.Generate(); - requestDocument.Data.Attributes.HasProducedEggs = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostChickenAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/chickens"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("hasProducedEggs").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.False)); - }); - } - - [Fact] - public async Task Can_clear_has_one_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestChicken.Data = null; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships.oldestChicken.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Fact] - public async Task Can_exclude_has_one_relationship_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestChicken = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("oldestChicken"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_has_one_relationship_without_document_registration_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FirstChicken = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'firstChicken'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Cannot_exclude_required_has_one_relationship_with_document_registration_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FirstChicken = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'firstChicken' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Can_exclude_has_many_relationship_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.AllChickens = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/henHouses"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("allChickens"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_has_many_relationship_with_document_registration_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.ChickensReadyForLaying = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'chickensReadyForLaying' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Cannot_exclude_required_has_many_relationship_without_document_registration_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePostRequestDocument requestDocument = _fakers.HenHousePostRequestDocument.Generate(); - requestDocument.Data.Relationships.ChickensReadyForLaying = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostHenHouseAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'chickensReadyForLaying'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Cannot_exclude_id_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPatchRequestDocument requestDocument = _fakers.ChickenPatchRequestDocument.Generate(); - requestDocument.Data.Id = default!; - - // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(999, requestDocument)); - - // Assert - await action.Should().ThrowAsync(); - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); - } - - [Fact] - public async Task Attributes_required_in_POST_request_are_not_required_in_PATCH_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - ChickenPatchRequestDocument requestDocument = _fakers.ChickenPatchRequestDocument.Generate(); - requestDocument.Data.Attributes.NameOfCurrentFarm = default!; - requestDocument.Data.Attributes.Weight = default!; - requestDocument.Data.Attributes.HasProducedEggs = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchChickenAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be($"http://localhost/chickens/{int.Parse(requestDocument.Data.Id)}"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("nameOfCurrentFarm"); - attributesObject.ShouldNotContainPath("weight"); - attributesObject.ShouldNotContainPath("hasProducedMilk"); - }); - } - - [Fact] - public async Task Relationships_required_in_POST_request_are_not_required_in_PATCH_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesDisabledClient(wrapper.HttpClient); - - HenHousePatchRequestDocument requestDocument = _fakers.HenHousePatchRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestChicken = default!; - requestDocument.Data.Relationships.FirstChicken = default!; - requestDocument.Data.Relationships.ChickensReadyForLaying = default!; - requestDocument.Data.Relationships.AllChickens = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchHenHouseAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be($"http://localhost/henHouses/{int.Parse(requestDocument.Data.Id)}"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("oldestChicken"); - relationshipsObject.ShouldNotContainPath("firstChicken"); - relationshipsObject.ShouldNotContainPath("favoriteChicken"); - relationshipsObject.ShouldNotContainPath("allChickens"); - }); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs deleted file mode 100644 index e3d01f54d4..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using FluentAssertions; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -public sealed class NullabilityTests -{ - [Theory] - [InlineData(nameof(CowAttributesInResponse.Name), NullabilityState.NotNull)] - [InlineData(nameof(CowAttributesInResponse.NameOfCurrentFarm), NullabilityState.NotNull)] - [InlineData(nameof(CowAttributesInResponse.NameOfPreviousFarm), NullabilityState.Nullable)] - [InlineData(nameof(CowAttributesInResponse.Nickname), NullabilityState.NotNull)] - [InlineData(nameof(CowAttributesInResponse.Age), NullabilityState.NotNull)] - [InlineData(nameof(CowAttributesInResponse.Weight), NullabilityState.NotNull)] - [InlineData(nameof(CowAttributesInResponse.TimeAtCurrentFarmInDays), NullabilityState.Nullable)] - [InlineData(nameof(CowAttributesInResponse.HasProducedMilk), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) - { - PropertyInfo[] properties = typeof(CowAttributesInResponse).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); - property.Should().HaveNullabilityState(expectedState); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs deleted file mode 100644 index c999cecef2..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledFaker.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Bogus; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -// @formatter:wrap_chained_method_calls chop_always -// @formatter:keep_existing_linebreaks true - -internal sealed class NullableReferenceTypesEnabledFaker -{ - private readonly Lazy> _lazyCowStablePostRequestDocumentFaker; - private readonly Lazy> _lazyCowStablePatchRequestDocumentFaker; - - private readonly Lazy> _lazyCowPostRequestDocumentFaker = new(() => - { - Faker attributesInPostRequestFaker = new Faker() - .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) - .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.NameOfPreviousFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.Nickname, faker => faker.Internet.UserName()) - .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) - .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) - .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) - .RuleFor(attributes => attributes.HasProducedMilk, _ => true); - - Faker dataInPostRequestFaker = new Faker() - .RuleFor(data => data.Attributes, _ => attributesInPostRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); - }); - - private readonly Lazy> _lazyCowPatchRequestDocumentFaker = new(() => - { - Faker attributesInPatchRequestFaker = new Faker() - .RuleFor(attributes => attributes.Name, faker => faker.Name.FirstName()) - .RuleFor(attributes => attributes.NameOfCurrentFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.NameOfPreviousFarm, faker => faker.Company.CompanyName()) - .RuleFor(attributes => attributes.Nickname, faker => faker.Internet.UserName()) - .RuleFor(attributes => attributes.Age, faker => faker.Random.Int(1, 20)) - .RuleFor(attributes => attributes.Weight, faker => faker.Random.Int(20, 50)) - .RuleFor(attributes => attributes.TimeAtCurrentFarmInDays, faker => faker.Random.Int(1, 356)) - .RuleFor(attributes => attributes.HasProducedMilk, _ => true); - - Faker dataInPatchRequestFaker = new Faker() - // @formatter:wrap_chained_method_calls chop_if_long - .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) - // @formatter:wrap_chained_method_calls restore - .RuleFor(data => data.Attributes, _ => attributesInPatchRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); - }); - - private readonly Lazy> _lazyToOneCowInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new CowIdentifier - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - })); - - private readonly Lazy> _lazyNullableToOneCowInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new CowIdentifier - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - })); - - private readonly Lazy> _lazyToManyCowInRequestFaker = new(() => - new Faker() - .RuleFor(relationship => relationship.Data, faker => new List - { - new() - { - // @formatter:wrap_chained_method_calls chop_if_long - Id = faker.Random.Int(1, 100).ToString() - // @formatter:wrap_chained_method_calls restore - } - })); - - public Faker CowPostRequestDocument => _lazyCowPostRequestDocumentFaker.Value; - public Faker CowPatchRequestDocument => _lazyCowPatchRequestDocumentFaker.Value; - public Faker CowStablePostRequestDocument => _lazyCowStablePostRequestDocumentFaker.Value; - public Faker CowStablePatchRequestDocument => _lazyCowStablePatchRequestDocumentFaker.Value; - - public NullableReferenceTypesEnabledFaker() - { - _lazyCowStablePostRequestDocumentFaker = new Lazy>(CreateCowStablePostRequestDocumentFaker); - _lazyCowStablePatchRequestDocumentFaker = new Lazy>(CreateCowStablePatchRequestDocumentFaker); - } - - private Faker CreateCowStablePostRequestDocumentFaker() - { - Faker relationshipsInPostRequestFaker = new Faker() - .RuleFor(relationships => relationships.OldestCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FirstCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AlbinoCow, _ => _lazyNullableToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FavoriteCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.CowsReadyForMilking, _ => _lazyToManyCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AllCows, _ => _lazyToManyCowInRequestFaker.Value.Generate()); - - Faker dataInPostRequestFaker = new Faker() - .RuleFor(data => data.Relationships, _ => relationshipsInPostRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPostRequestFaker.Generate()); - } - - private Faker CreateCowStablePatchRequestDocumentFaker() - { - Faker relationshipsInPatchRequestFaker = new Faker() - .RuleFor(relationships => relationships.OldestCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FirstCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AlbinoCow, _ => _lazyNullableToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.FavoriteCow, _ => _lazyToOneCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.CowsReadyForMilking, _ => _lazyToManyCowInRequestFaker.Value.Generate()) - .RuleFor(relationships => relationships.AllCows, _ => _lazyToManyCowInRequestFaker.Value.Generate()); - - Faker dataInPatchRequestFaker = new Faker() - // @formatter:wrap_chained_method_calls chop_if_long - .RuleFor(data => data.Id, faker => faker.Random.Int(1, 100).ToString()) - // @formatter:wrap_chained_method_calls restore - .RuleFor(data => data.Relationships, _ => relationshipsInPatchRequestFaker.Generate()); - - return new Faker() - .RuleFor(document => document.Data, _ => dataInPatchRequestFaker.Generate()); - } -} diff --git a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs b/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs deleted file mode 100644 index 0c36b0a137..0000000000 --- a/test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/RequestTests.cs +++ /dev/null @@ -1,728 +0,0 @@ -using System.Net; -using System.Text.Json; -using FluentAssertions; -using FluentAssertions.Specialized; -using JsonApiDotNetCore.Middleware; -using Microsoft.Net.Http.Headers; -using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.GeneratedCode; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiClientTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -public sealed class RequestTests -{ - private readonly NullableReferenceTypesEnabledFaker _fakers = new(); - - [Fact] - public async Task Cannot_exclude_non_nullable_reference_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Name = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'name' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Cannot_exclude_required_non_nullable_reference_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.NameOfCurrentFarm = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'nameOfCurrentFarm' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_exclude_nullable_reference_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.NameOfPreviousFarm = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("nameOfPreviousFarm"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_nullable_reference_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Nickname = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'nickname' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_set_default_value_to_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Age = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("age").With(attribute => attribute.ShouldBeInteger(0)); - }); - } - - [Fact] - public async Task Can_exclude_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Age = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("age"); - }); - } - - [Fact] - public async Task Can_set_default_value_to_required_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Weight = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("weight").With(attribute => attribute.ShouldBeInteger(0)); - }); - } - - [Fact] - public async Task Cannot_exclude_required_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.Weight = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'weight' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Can_clear_nullable_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = null; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument, - cow => cow.TimeAtCurrentFarmInDays)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("timeAtCurrentFarmInDays").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Fact] - public async Task Can_exclude_nullable_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.TimeAtCurrentFarmInDays = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Can_set_default_value_to_required_nullable_value_type_attribute() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.HasProducedMilk = default; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cows"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldContainPath("hasProducedMilk").With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.False)); - }); - } - - [Fact] - public async Task Cannot_exclude_required_nullable_value_type_attribute_in_POST_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPostRequestDocument requestDocument = _fakers.CowPostRequestDocument.Generate(); - requestDocument.Data.Attributes.HasProducedMilk = default; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'hasProducedMilk' must have a value because it is required. Path 'data.attributes'."); - } - } - - [Fact] - public async Task Cannot_exclude_has_one_relationship_in_POST_request_with_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestCow = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'oldestCow' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Cannot_exclude_has_one_relationship_in_POST_request_without_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestCow = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'oldestCow'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Cannot_exclude_required_has_one_relationship_in_POST_request_with_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FirstCow = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'firstCow' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Cannot_exclude_required_has_one_relationship_in_POST_request_without_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FirstCow = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'firstCow'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_clear_nullable_has_one_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.AlbinoCow.Data = null; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships.albinoCow.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Fact] - public async Task Can_exclude_nullable_has_one_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.AlbinoCow = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("albinoCow"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_nullable_has_one_relationship_in_POST_request_with_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FavoriteCow = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'favoriteCow' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Cannot_exclude_required_nullable_has_one_relationship_in_POST_request_without_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.FavoriteCow = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'favoriteCow'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Can_exclude_has_many_relationship() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.CowsReadyForMilking = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be("http://localhost/cowStables"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("cowsReadyForMilking"); - }); - } - - [Fact] - public async Task Cannot_exclude_required_has_many_relationship_in_POST_request_with_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.AllCows = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Ignored property 'allCows' must have a value because it is required. Path 'data.relationships'."); - } - } - - [Fact] - public async Task Cannot_exclude_required_has_many_relationship_in_POST_request_without_document_registration() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePostRequestDocument requestDocument = _fakers.CowStablePostRequestDocument.Generate(); - requestDocument.Data.Relationships.AllCows = default!; - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostCowStableAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'allCows'. Property requires a value. Path 'data.relationships'."); - } - - [Fact] - public async Task Cannot_exclude_id_when_performing_PATCH() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPatchRequestDocument requestDocument = _fakers.CowPatchRequestDocument.Generate(); - requestDocument.Data.Id = default!; - - // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(999, requestDocument)); - - // Assert - await action.Should().ThrowAsync(); - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); - } - - [Fact] - public async Task Attributes_required_in_POST_request_are_not_required_in_PATCH_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowPatchRequestDocument requestDocument = _fakers.CowPatchRequestDocument.Generate(); - requestDocument.Data.Attributes.Name = default!; - requestDocument.Data.Attributes.NameOfCurrentFarm = default!; - requestDocument.Data.Attributes.Nickname = default!; - requestDocument.Data.Attributes.Weight = default!; - requestDocument.Data.Attributes.HasProducedMilk = default!; - - using (apiClient.OmitDefaultValuesForAttributesInRequestDocument(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be($"http://localhost/cows/{int.Parse(requestDocument.Data.Id)}"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.attributes").With(attributesObject => - { - attributesObject.ShouldNotContainPath("name"); - attributesObject.ShouldNotContainPath("nameOfCurrentFarm"); - attributesObject.ShouldNotContainPath("nickname"); - attributesObject.ShouldNotContainPath("weight"); - attributesObject.ShouldNotContainPath("hasProducedMilk"); - }); - } - - [Fact] - public async Task Relationships_required_in_POST_request_are_not_required_in_PATCH_request() - { - // Arrange - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NullableReferenceTypesEnabledClient(wrapper.HttpClient); - - CowStablePatchRequestDocument requestDocument = _fakers.CowStablePatchRequestDocument.Generate(); - requestDocument.Data.Relationships.OldestCow = default!; - requestDocument.Data.Relationships.FirstCow = default!; - requestDocument.Data.Relationships.FavoriteCow = default!; - requestDocument.Data.Relationships.AllCows = default!; - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchCowStableAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - - // Assert - wrapper.Request.ShouldNotBeNull(); - wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); - wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be($"http://localhost/cowStables/{int.Parse(requestDocument.Data.Id)}"); - wrapper.Request.Content.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); - wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); - - JsonElement document = wrapper.ParseRequestBody(); - - document.ShouldContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.ShouldNotContainPath("oldestCow"); - relationshipsObject.ShouldNotContainPath("firstCow"); - relationshipsObject.ShouldNotContainPath("favoriteCow"); - relationshipsObject.ShouldNotContainPath("allCows"); - }); - } -} diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json index 4b6c140fc4..f8c1dcfcb1 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json +++ b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json @@ -1970,6 +1970,9 @@ "additionalProperties": false }, "airplane-attributes-in-response": { + "required": [ + "name" + ], "type": "object", "properties": { "name": { @@ -2238,6 +2241,10 @@ "additionalProperties": false }, "flight-attendant-attributes-in-response": { + "required": [ + "email-address", + "profile-image-url" + ], "type": "object", "properties": { "email-address": { @@ -2557,6 +2564,10 @@ "additionalProperties": false }, "flight-attributes-in-response": { + "required": [ + "final-destination", + "services-on-board" + ], "type": "object", "properties": { "final-destination": { @@ -2815,6 +2826,9 @@ "additionalProperties": false }, "flight-relationships-in-response": { + "required": [ + "purser" + ], "type": "object", "properties": { "cabin-crew-members": { diff --git a/test/OpenApiTests/OpenApiTestContext.cs b/test/OpenApiTests/OpenApiTestContext.cs index 78bbd0f08d..e54d09a269 100644 --- a/test/OpenApiTests/OpenApiTestContext.cs +++ b/test/OpenApiTests/OpenApiTestContext.cs @@ -2,7 +2,6 @@ using System.Text.Json; using JetBrains.Annotations; using TestBuildingBlocks; -using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace OpenApiTests; diff --git a/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs b/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs new file mode 100644 index 0000000000..0d7ddf5f1c --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs @@ -0,0 +1,9 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +namespace OpenApiTests.ResourceFieldValidation; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public class EmptyResource : Identifiable +{ +} diff --git a/test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs similarity index 90% rename from test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs rename to test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs index f3e39495c3..0bea8b4d58 100644 --- a/test/OpenApiTests/ResourceFieldsValidation/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.Configuration; using TestBuildingBlocks; -namespace OpenApiTests.ResourceFieldsValidation; +namespace OpenApiTests.ResourceFieldValidation; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class ModelStateValidationDisabledStartup : OpenApiStartup diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..d6a4d83647 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -0,0 +1,182 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> + _testContext; + + public NullabilityTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled"; + } + + [Fact] + public async Task Schema_property_for_reference_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("referenceType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_for_required_reference_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredReferenceType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_for_value_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("valueType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_for_required_value_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_for_nullable_value_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_for_required_nullable_value_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_for_to_one_relationship_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("toOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_one_relationship_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Fact] + public async Task Schema_property_for_to_many_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_many_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs new file mode 100644 index 0000000000..218af9b286 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs @@ -0,0 +1,180 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; + +public sealed class RequiredTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> + _testContext; + + public RequiredTests( + OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_for_reference_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.referenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("referenceType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_reference_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredReferenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_for_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.valueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_value_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_nullable_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_nullable_value_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_to_one_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.toOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toOne")); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_one_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredToOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToOne")); + }); + } + + [Fact] + public async Task Schema_property_for_to_many_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.toMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_many_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToMany")); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_schema_properties_for_relationships_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs similarity index 56% rename from test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs index 4620ebd844..3e2f4d4b08 100644 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -3,7 +3,7 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; public sealed class NullabilityTests : IClassFixture, NullableReferenceTypesDisabledDbContext>> @@ -14,21 +14,20 @@ public NullabilityTests(OpenApiTestContext(); - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesDisabled"; + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled"; } [Fact] - public async Task Schema_property_is_nullable_for_reference_type_attribute() + public async Task Schema_property_for_reference_type_attribute_is_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("name").With(schemaProperty => + schemaProperties.ShouldContainPath("referenceType").With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); @@ -36,15 +35,15 @@ public async Task Schema_property_is_nullable_for_reference_type_attribute() } [Fact] - public async Task Schema_property_is_not_nullable_for_required_reference_type_attribute() + public async Task Schema_property_for_required_reference_type_attribute_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => + schemaProperties.ShouldContainPath("requiredReferenceType").With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); @@ -52,15 +51,15 @@ public async Task Schema_property_is_not_nullable_for_required_reference_type_at } [Fact] - public async Task Schema_property_is_not_nullable_for_value_type_attribute() + public async Task Schema_property_for_value_type_attribute_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("age").With(schemaProperty => + schemaProperties.ShouldContainPath("valueType").With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); @@ -68,15 +67,15 @@ public async Task Schema_property_is_not_nullable_for_value_type_attribute() } [Fact] - public async Task Schema_property_is_not_nullable_for_required_value_type_attribute() + public async Task Schema_property_for_required_value_type_attribute_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("weight").With(schemaProperty => + schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); @@ -84,15 +83,15 @@ public async Task Schema_property_is_not_nullable_for_required_value_type_attrib } [Fact] - public async Task Schema_property_is_nullable_for_nullable_value_type_attribute() + public async Task Schema_property_for_nullable_value_type_attribute_is_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => + schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); @@ -100,15 +99,15 @@ public async Task Schema_property_is_nullable_for_nullable_value_type_attribute( } [Fact] - public async Task Schema_property_is_not_nullable_for_required_nullable_value_type_attribute() + public async Task Schema_property_for_required_nullable_value_type_attribute_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInResponse.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("hasProducedEggs").With(schemaProperty => + schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); @@ -116,15 +115,15 @@ public async Task Schema_property_is_not_nullable_for_required_nullable_value_ty } [Fact] - public async Task Schema_property_is_nullable_for_has_one_relationship() + public async Task Schema_property_for_to_one_relationship_is_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("oldestChicken.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath("toOne.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); @@ -132,15 +131,15 @@ public async Task Schema_property_is_nullable_for_has_one_relationship() } [Fact] - public async Task Schema_property_is_not_nullable_for_required_has_one_relationship() + public async Task Schema_property_for_required_to_one_relationship_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("firstChicken.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath("requiredToOne.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); @@ -148,15 +147,15 @@ public async Task Schema_property_is_not_nullable_for_required_has_one_relations } [Fact] - public async Task Schema_property_is_not_nullable_for_has_many_relationship() + public async Task Schema_property_for_to_many_relationship_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("chickensReadyForLaying.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); @@ -164,15 +163,15 @@ public async Task Schema_property_is_not_nullable_for_has_many_relationship() } [Fact] - public async Task Schema_property_is_not_nullable_for_required_has_many_relationship() + public async Task Schema_property_for_required_to_many_relationship_is_not_nullable() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.properties").With(schemaProperties => + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("allChickens.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs new file mode 100644 index 0000000000..3af1cdc0bc --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs @@ -0,0 +1,181 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; + +public sealed class RequiredTests + : IClassFixture, NullableReferenceTypesDisabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + + public RequiredTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_for_reference_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.referenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("referenceType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_reference_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredReferenceType"); + + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_for_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.valueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_nullable_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_required_nullable_value_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); + + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_for_to_one_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.toOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toOne")); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_one_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredToOne"); + + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToOne")); + }); + } + + [Fact] + public async Task Schema_property_for_to_many_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.toMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); + }); + } + + [Fact] + public async Task Schema_property_for_required_to_many_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredToMany")); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_schema_properties_for_relationships_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs new file mode 100644 index 0000000000..4a297cf3e9 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs @@ -0,0 +1,48 @@ +#nullable disable + +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public sealed class NrtDisabledResource : Identifiable +{ + [Attr] + public string ReferenceType { get; set; } + + [Attr] + [Required] + public string RequiredReferenceType { get; set; } + + [Attr] + public int ValueType { get; set; } + + [Attr] + [Required] + public int RequiredValueType { get; set; } + + [Attr] + public int? NullableValueType { get; set; } + + [Attr] + [Required] + public int? RequiredNullableValueType { get; set; } + + [HasOne] + public EmptyResource ToOne { get; set; } + + [Required] + [HasOne] + public EmptyResource RequiredToOne { get; set; } + + [HasMany] + public ICollection ToMany { get; set; } = new HashSet(); + + [Required] + [HasMany] + public ICollection RequiredToMany { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs new file mode 100644 index 0000000000..1e8691d9de --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs @@ -0,0 +1,35 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; + +// @formatter:wrap_chained_method_calls chop_always + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext +{ + public DbSet NrtDisabledResources => Set(); + + public NullableReferenceTypesDisabledDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.ToOne); + + builder.Entity() + .HasOne(resource => resource.RequiredToOne); + + builder.Entity() + .HasMany(resource => resource.ToMany); + + builder.Entity() + .HasMany(resource => resource.RequiredToMany); + + base.OnModelCreating(builder); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs new file mode 100644 index 0000000000..520a9e0172 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -0,0 +1,100 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> + _testContext; + + public NullabilityTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled"; + } + + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + public async Task Schema_property_that_describes_attribute_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Theory] + [InlineData("nullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_that_describes_attribute_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_that_describes_relationship_is_not_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Theory] + [InlineData("nullableToOne")] + [InlineData("requiredNullableToOne")] + public async Task Schema_property_that_describes_relationship_is_nullable(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs new file mode 100644 index 0000000000..c622636ba6 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs @@ -0,0 +1,100 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; + +public sealed class RequiredTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> + _testContext; + + public RequiredTests( + OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("nullableReferenceType")] + [InlineData("valueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_that_describes_attribute_in_create_resource_is_not_required(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_that_describes_attribute_in_create_resource_is_required(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] + [InlineData("requiredToMany")] + public async Task Schema_property_that_describes_relationship_in_create_resource_is_required(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + }); + } + + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("nullableToOne")] + [InlineData("toMany")] + public async Task Schema_property_that_describes_relationship_in_create_resource_is_not_required(string jsonPropertyName) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + }); + } + + [Fact] + public async Task No_schema_properties_for_relationships_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs new file mode 100644 index 0000000000..9a8cc9b69c --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -0,0 +1,244 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +public sealed class NullabilityTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled"; + } + + [Fact] + public async Task Schema_property_that_describes_non_nullable_reference_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nonNullableReferenceType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_non_nullable_reference_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNonNullableReferenceType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_reference_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nullableReferenceType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_reference_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNullableReferenceType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_value_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("valueType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_value_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_value_type_attribute_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => + { + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_value_type_attribute_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => + { + schemaProperty.ShouldNotContainPath("nullable"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_non_nullable_to_one_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nonNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_non_nullable_to_one_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNonNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_to_one_relationship_is_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("nullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_to_one_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_to_many_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_to_many_relationship_is_not_nullable() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + { + schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => + { + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + }); + }); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs new file mode 100644 index 0000000000..467d72684d --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs @@ -0,0 +1,234 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; + +public sealed class RequiredTests + : IClassFixture, NullableReferenceTypesEnabledDbContext>> +{ + private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + + public RequiredTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + } + + [Fact] + public async Task Schema_property_that_describes_non_nullable_reference_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.nonNullableReferenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("nonNullableReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_non_nullable_reference_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredNonNullableReferenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNonNullableReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_reference_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.nullableReferenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_reference_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredNullableReferenceType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableReferenceType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.valueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredValueType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_value_type_attribute_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_value_type_attribute_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + { + attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_non_nullable_to_one_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.nonNullableToOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("nonNullableToOne")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_non_nullable_to_one_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredNonNullableToOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNonNullableToOne")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_nullable_to_one_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.nullableToOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableToOne")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_nullable_to_one_relationship_is_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredNullableToOne"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableToOne")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_to_many_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.toMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); + }); + } + + [Fact] + public async Task Schema_property_that_describes_required_to_many_relationship_is_not_required() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + { + relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredToMany")); + }); + } + + [Fact] + public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_schema_properties_for_relationships_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + } +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs new file mode 100644 index 0000000000..8908f2a50f --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs @@ -0,0 +1,60 @@ +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public sealed class NrtEnabledResource : Identifiable +{ + [Attr] + public string NonNullableReferenceType { get; set; } = null!; + + [Attr] + [Required] + public string RequiredNonNullableReferenceType { get; set; } = null!; + + [Attr] + public string? NullableReferenceType { get; set; } + + [Attr] + [Required] + public string? RequiredNullableReferenceType { get; set; } + + [Attr] + public int ValueType { get; set; } + + [Attr] + [Required] + public int RequiredValueType { get; set; } + + [Attr] + public int? NullableValueType { get; set; } + + [Attr] + [Required] + public int? RequiredNullableValueType { get; set; } + + [HasOne] + public EmptyResource NonNullableToOne { get; set; } = null!; + + [Required] + [HasOne] + public EmptyResource RequiredNonNullableToOne { get; set; } = null!; + + [HasOne] + public EmptyResource? NullableToOne { get; set; } + + [Required] + [HasOne] + public EmptyResource? RequiredNullableToOne { get; set; } + + [HasMany] + public ICollection ToMany { get; set; } = new HashSet(); + + [Required] + [HasMany] + public ICollection RequiredToMany { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs new file mode 100644 index 0000000000..54f1620db5 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs @@ -0,0 +1,41 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; + +// @formatter:wrap_chained_method_calls chop_always + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext +{ + public DbSet NullableReferenceTypesEnabledResources => Set(); + + public NullableReferenceTypesEnabledDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .HasOne(resource => resource.NonNullableToOne); + + builder.Entity() + .HasOne(resource => resource.RequiredNonNullableToOne); + + builder.Entity() + .HasOne(resource => resource.NullableToOne); + + builder.Entity() + .HasOne(resource => resource.RequiredNullableToOne); + + builder.Entity() + .HasMany(resource => resource.ToMany); + + builder.Entity() + .HasMany(resource => resource.RequiredToMany); + + base.OnModelCreating(builder); + } +} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs deleted file mode 100644 index 53c1a17f98..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Chicken.cs +++ /dev/null @@ -1,34 +0,0 @@ -#nullable disable - -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] -public sealed class Chicken : Identifiable -{ - [Attr] - public string Name { get; set; } - - [Attr] - [Required] - public string NameOfCurrentFarm { get; set; } - - [Attr] - public int Age { get; set; } - - [Attr] - [Required] - public int Weight { get; set; } - - [Attr] - public int? TimeAtCurrentFarmInDays { get; set; } - - [Attr] - [Required] - public bool? HasProducedEggs { get; set; } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs deleted file mode 100644 index 8c24664eec..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/HenHouse.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable - -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] -public sealed class HenHouse : Identifiable -{ - [HasOne] - public Chicken OldestChicken { get; set; } - - [Required] - [HasOne] - public Chicken FirstChicken { get; set; } - - [HasMany] - public ICollection AllChickens { get; set; } = new HashSet(); - - [Required] - [HasMany] - public ICollection ChickensReadyForLaying { get; set; } = new HashSet(); -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs deleted file mode 100644 index 6562979252..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ /dev/null @@ -1,36 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled; - -// @formatter:wrap_chained_method_calls chop_always - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext -{ - public DbSet Chicken => Set(); - public DbSet HenHouse => Set(); - - public NullableReferenceTypesDisabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.OldestChicken); - - builder.Entity() - .HasOne(resource => resource.FirstChicken); - - builder.Entity() - .HasMany(resource => resource.AllChickens); - - builder.Entity() - .HasMany(resource => resource.ChickensReadyForLaying); - - base.OnModelCreating(builder); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs deleted file mode 100644 index d2d7669b34..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.Required; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Fact] - public async Task Schema_property_is_not_required_for_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("name"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nameOfCurrentFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("age"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("weight"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("hasProducedEggs"); - }); - } - - [Fact] - public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("oldestChicken"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain("firstChicken"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("allChickens"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain("chickensReadyForLaying"); - }); - } - - [Fact] - public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs deleted file mode 100644 index 24b01433c2..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesDisabled/Required/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesDisabled.Required; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Fact] - public async Task Schema_property_is_not_required_for_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("name"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nameOfCurrentFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("age"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("weight"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.chickenAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("hasProducedEggs"); - }); - } - - [Fact] - public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.chickenAttributesInPatchRequest.required"); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("oldestChicken"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain("firstChicken"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("allChickens"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.henHouseRelationshipsInPostRequest.required").With(propertySet => - { - var requiredAttributes = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredAttributes.Should().Contain("chickensReadyForLaying"); - }); - } - - [Fact] - public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.henHouseRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs deleted file mode 100644 index 4a09687602..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Cow.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] -public sealed class Cow : Identifiable -{ - [Attr] - public string Name { get; set; } = null!; - - [Attr] - [Required] - public string NameOfCurrentFarm { get; set; } = null!; - - [Attr] - public string? NameOfPreviousFarm { get; set; } - - [Attr] - [Required] - public string? Nickname { get; set; } - - [Attr] - public int Age { get; set; } - - [Attr] - [Required] - public int Weight { get; set; } - - [Attr] - public int? TimeAtCurrentFarmInDays { get; set; } - - [Attr] - [Required] - public bool? HasProducedMilk { get; set; } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs deleted file mode 100644 index 6b7af3c5d0..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/CowStable.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ResourceFieldsValidation")] -public sealed class CowStable : Identifiable -{ - [HasOne] - public Cow OldestCow { get; set; } = null!; - - [Required] - [HasOne] - public Cow FirstCow { get; set; } = null!; - - [HasOne] - public Cow? AlbinoCow { get; set; } - - [Required] - [HasOne] - public Cow? FavoriteCow { get; set; } - - [HasMany] - public ICollection CowsReadyForMilking { get; set; } = new HashSet(); - - [Required] - [HasMany] - public ICollection AllCows { get; set; } = new HashSet(); -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs deleted file mode 100644 index 82b2a94b8c..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullabilityTests.cs +++ /dev/null @@ -1,245 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldsValidation/NullableReferenceTypesEnabled"; - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("name").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nameOfCurrentFarm").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_nullable_for_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nameOfPreviousFarm").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nickname").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("age").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("weight").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_nullable_for_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("timeAtCurrentFarmInDays").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("hasProducedMilk").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("oldestCow.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("firstCow.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_nullable_for_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("albinoCow.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("favoriteCow.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("cowsReadyForMilking.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_is_not_nullable_for_required_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("allCows.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs deleted file mode 100644 index d8ad163bdd..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ /dev/null @@ -1,42 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled; - -// @formatter:wrap_chained_method_calls chop_always - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext -{ - public DbSet Cow => Set(); - public DbSet CowStable => Set(); - - public NullableReferenceTypesEnabledDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .HasOne(resource => resource.OldestCow); - - builder.Entity() - .HasOne(resource => resource.FirstCow); - - builder.Entity() - .HasOne(resource => resource.AlbinoCow); - - builder.Entity() - .HasOne(resource => resource.FavoriteCow); - - builder.Entity() - .HasMany(resource => resource.AllCows); - - builder.Entity() - .HasMany(resource => resource.CowsReadyForMilking); - - base.OnModelCreating(builder); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs deleted file mode 100644 index 17ac968dee..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationDisabledTests.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.Required; - -public sealed class ModelStateValidationDisabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; - - public ModelStateValidationDisabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Fact] - public async Task Schema_property_is_required_for_non_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("name"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_non_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nameOfCurrentFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("nameOfPreviousFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nickname"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("age"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("weight"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("hasProducedMilk"); - }); - } - - [Fact] - public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableAttributesInPatchRequest.required"); - } - - [Fact] - public async Task Schema_property_is_required_for_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("oldestCow"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("firstCow"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("albinoCow"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("favoriteCow"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("cowsReadyForMilking"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("allCows"); - }); - } - - [Fact] - public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} diff --git a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs b/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs deleted file mode 100644 index 24146e7bba..0000000000 --- a/test/OpenApiTests/ResourceFieldsValidation/NullableReferenceTypesEnabled/Required/ModelStateValidationEnabledTests.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using TestBuildingBlocks; -using Xunit; - -namespace OpenApiTests.ResourceFieldsValidation.NullableReferenceTypesEnabled.Required; - -public sealed class ModelStateValidationEnabledTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> -{ - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; - - public ModelStateValidationEnabledTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) - { - _testContext = testContext; - - testContext.UseController(); - testContext.UseController(); - } - - [Fact] - public async Task Schema_property_is_required_for_non_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("name"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_non_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nameOfCurrentFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("nameOfPreviousFarm"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_reference_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("nickname"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("age"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("weight"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("timeAtCurrentFarmInDays"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_value_type_attribute() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowAttributesInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("hasProducedMilk"); - }); - } - - [Fact] - public async Task No_schema_properties_for_attributes_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableAttributesInPatchRequest.required"); - } - - [Fact] - public async Task Schema_property_is_required_for_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("oldestCow"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("firstCow"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("albinoCow"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_nullable_has_one_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("favoriteCow"); - }); - } - - [Fact] - public async Task Schema_property_is_not_required_for_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().NotContain("cowsReadyForMilking"); - }); - } - - [Fact] - public async Task Schema_property_is_required_for_required_has_many_relationship() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.cowStableRelationshipsInPostRequest.required").With(propertySet => - { - var requiredProperties = JsonSerializer.Deserialize>(propertySet.GetRawText()); - - requiredProperties.Should().Contain("allCows"); - }); - } - - [Fact] - public async Task No_schema_properties_for_relationships_are_required_in_PATCH_request() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldNotContainPath("components.schemas.cowStableRelationshipsInPatchRequest.required"); - } -} diff --git a/test/TestBuildingBlocks/JsonElementExtensions.cs b/test/TestBuildingBlocks/JsonElementExtensions.cs index ee5c362489..6eafecc4ca 100644 --- a/test/TestBuildingBlocks/JsonElementExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementExtensions.cs @@ -32,6 +32,22 @@ public static void ShouldBeString(this JsonElement source, string value) source.GetString().Should().Be(value); } + public static void ShouldBeArrayWithElement(this JsonElement source, string value) + { + source.ValueKind.Should().Be(JsonValueKind.Array); + + var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); + deserializedCollection.Should().Contain(value); + } + + public static void ShouldBeArrayWithoutElement(this JsonElement source, string value) + { + source.ValueKind.Should().Be(JsonValueKind.Array); + + var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); + deserializedCollection.Should().NotContain(value); + } + public static void ShouldBeInteger(this JsonElement source, int value) { source.ValueKind.Should().Be(JsonValueKind.Number); From 63ba43da0c171d0b39f211894df5934b77404146 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 4 Jan 2023 13:35:38 +0100 Subject: [PATCH 23/66] Remove non-sensical testcases. Add caching in ObjectExtensions. --- test/OpenApiClientTests/ObjectExtensions.cs | 28 +++++-- .../CreateResourceTests.cs | 83 ------------------- .../ResourceFieldValidationFakers.cs | 1 - .../ResourceFieldValidationFakers.cs | 1 - .../CreateResourceTests.cs | 4 - .../ResourceFieldValidationFakers.cs | 1 - .../ResourceFieldValidationFakers.cs | 1 - 7 files changed, 22 insertions(+), 97 deletions(-) diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs index cdf729c09b..7225702d5c 100644 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Reflection; using JsonApiDotNetCore.OpenApi.Client; @@ -5,14 +6,29 @@ namespace OpenApiClientTests; internal static class ObjectExtensions { + private static readonly ConcurrentDictionary PropertyInfoCache = new(); + public static object? GetPropertyValue(this object source, string propertyName) { ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); + + return PropertyInfoCache[cacheKey].GetValue(source); + } + + private static string EnsurePropertyInfoIsCached(object source, string propertyName) + { + string cacheKey = $"{source.GetType().FullName}-{propertyName}"; + + if (!PropertyInfoCache.ContainsKey(cacheKey)) + { + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + PropertyInfoCache[cacheKey] = propertyInfo; + } - return propertyInfo.GetValue(source); + return cacheKey; } public static void SetPropertyValue(this object source, string propertyName, object? value) @@ -20,9 +36,9 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); - propertyInfo.SetValue(source, value); + PropertyInfoCache[cacheKey].SetValue(source, value); } public static object? GetDefaultValueForProperty(this object source, string propertyName) @@ -30,8 +46,8 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); + string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); - return Activator.CreateInstance(propertyInfo.PropertyType); + return Activator.CreateInstance(PropertyInfoCache[cacheKey].PropertyType); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs index 8888d8f56d..5dfa347b05 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs @@ -442,87 +442,4 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati relationshipsObject.ShouldNotContainPath(jsonPropertyName); }); } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne", Skip = "Known limitation")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne", Skip = "Known limitation")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs index 1bc729cbd9..817344c11c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs @@ -1,6 +1,5 @@ using Bogus; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; -using TestBuildingBlocks; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs index 2960441a6c..e3dbbd0d36 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs @@ -1,6 +1,5 @@ using Bogus; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; -using TestBuildingBlocks; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs index 27e9c06b5c..0ca3a6fb82 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs @@ -524,8 +524,6 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne", Skip = "Known limitation")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange @@ -570,8 +568,6 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne", Skip = "Known limitation")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany", Skip = "Known limitation")] public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs index 1c31f040b8..b0c9c260c7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs @@ -1,6 +1,5 @@ using Bogus; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; -using TestBuildingBlocks; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs index 8389b8bd2c..c032f0c073 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs @@ -1,6 +1,5 @@ using Bogus; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; -using TestBuildingBlocks; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; From 3e9708a28076fb2895c60a4ba3044f4745fd77e7 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 5 Jan 2023 21:13:31 +0100 Subject: [PATCH 24/66] Remove overlooked code duplication in OpenApiTests, revert reflection caching in object extension --- test/OpenApiClientTests/ObjectExtensions.cs | 28 +-- .../NullabilityTests.cs | 130 ++---------- .../RequiredTests.cs | 130 +++--------- .../NullabilityTests.cs | 130 ++---------- .../RequiredTests.cs | 133 +++--------- .../NullabilityTests.cs | 44 ++-- .../RequiredTests.cs | 40 ++-- .../NullabilityTests.cs | 198 +++--------------- .../RequiredTests.cs | 190 +++-------------- 9 files changed, 211 insertions(+), 812 deletions(-) diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs index 7225702d5c..9bf15b6ac7 100644 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -1,4 +1,3 @@ -using System.Collections.Concurrent; using System.Reflection; using JsonApiDotNetCore.OpenApi.Client; @@ -6,29 +5,14 @@ namespace OpenApiClientTests; internal static class ObjectExtensions { - private static readonly ConcurrentDictionary PropertyInfoCache = new(); - public static object? GetPropertyValue(this object source, string propertyName) { ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); - - return PropertyInfoCache[cacheKey].GetValue(source); - } - - private static string EnsurePropertyInfoIsCached(object source, string propertyName) - { - string cacheKey = $"{source.GetType().FullName}-{propertyName}"; - - if (!PropertyInfoCache.ContainsKey(cacheKey)) - { - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(attribute => attribute.Name == propertyName); - PropertyInfoCache[cacheKey] = propertyInfo; - } + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - return cacheKey; + return propertyInfo.GetValue(source); } public static void SetPropertyValue(this object source, string propertyName, object? value) @@ -36,9 +20,9 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - PropertyInfoCache[cacheKey].SetValue(source, value); + propertyInfo.SetValue(source, value); } public static object? GetDefaultValueForProperty(this object source, string propertyName) @@ -46,8 +30,8 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - string cacheKey = EnsurePropertyInfoIsCached(source, propertyName); + PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - return Activator.CreateInstance(PropertyInfoCache[cacheKey].PropertyType); + return Activator.CreateInstance(propertyInfo.PropertyType); } } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs index d6a4d83647..40c6c5558a 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -20,8 +20,12 @@ public NullabilityTests( testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled"; } - [Fact] - public async Task Schema_property_for_reference_type_attribute_is_nullable() + [Theory] + [InlineData("referenceType")] + [InlineData("requiredReferenceType")] + [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -29,15 +33,17 @@ public async Task Schema_property_for_reference_type_attribute_is_nullable() // Assert document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("referenceType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } - [Fact] - public async Task Schema_property_for_required_reference_type_attribute_is_nullable() + [Theory] + [InlineData("valueType")] + [InlineData("requiredValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -45,79 +51,17 @@ public async Task Schema_property_for_required_reference_type_attribute_is_nulla // Assert document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredReferenceType").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_for_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("valueType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); }); } - [Fact] - public async Task Schema_property_for_required_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_nullable_value_type_attribute_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_for_required_nullable_value_type_attribute_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_for_to_one_relationship_is_nullable() + [Theory] + [InlineData("toOne")] + [InlineData("requiredToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -125,47 +69,17 @@ public async Task Schema_property_for_to_one_relationship_is_nullable() // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("toOne.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } - [Fact] - public async Task Schema_property_for_required_to_one_relationship_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredToOne.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_to_many_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_required_to_many_relationship_is_not_nullable() + [Theory] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -173,7 +87,7 @@ public async Task Schema_property_for_required_to_many_relationship_is_not_nulla // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs index 218af9b286..5688462fb9 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs @@ -18,64 +18,11 @@ public RequiredTests( testContext.UseController(); } - [Fact] - public async Task Schema_property_for_reference_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.referenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("referenceType")); - }); - } - - [Fact] - public async Task Schema_property_for_required_reference_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredReferenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredReferenceType")); - }); - } - - [Fact] - public async Task Schema_property_for_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.valueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); - }); - } - - [Fact] - public async Task Schema_property_for_required_value_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredValueType")); - }); - } - - [Fact] - public async Task Schema_property_for_nullable_value_type_attribute_is_not_required() + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -83,13 +30,16 @@ public async Task Schema_property_for_nullable_value_type_attribute_is_not_requi // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_required_nullable_value_type_attribute_is_required() + [Theory] + [InlineData("referenceType")] + [InlineData("valueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -97,13 +47,15 @@ public async Task Schema_property_for_required_nullable_value_type_attribute_is_ // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_to_one_relationship_is_not_required() + [Theory] + [InlineData("requiredToOne")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -111,41 +63,15 @@ public async Task Schema_property_for_to_one_relationship_is_not_required() // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.toOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toOne")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_required_to_one_relationship_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.requiredToOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToOne")); - }); - } - - [Fact] - public async Task Schema_property_for_to_many_relationship_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.toMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); - }); - } - - [Fact] - public async Task Schema_property_for_required_to_many_relationship_is_required() + [Theory] + [InlineData("toOne")] + [InlineData("toMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -153,13 +79,13 @@ public async Task Schema_property_for_required_to_many_relationship_is_required( // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToMany")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } [Fact] - public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -169,7 +95,7 @@ public async Task No_schema_properties_for_attributes_are_required_when_updating } [Fact] - public async Task No_schema_properties_for_relationships_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs index 3e2f4d4b08..dcbcc572ab 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -18,8 +18,10 @@ public NullabilityTests(OpenApiTestContext { - schemaProperties.ShouldContainPath("referenceType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } - [Fact] - public async Task Schema_property_for_required_reference_type_attribute_is_not_nullable() + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -43,79 +49,16 @@ public async Task Schema_property_for_required_reference_type_attribute_is_not_n // Assert document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredReferenceType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); }); } - [Fact] - public async Task Schema_property_for_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("valueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_required_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_nullable_value_type_attribute_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_for_required_nullable_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_to_one_relationship_is_nullable() + [Theory] + [InlineData("toOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -123,47 +66,18 @@ public async Task Schema_property_for_to_one_relationship_is_nullable() // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("toOne.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } - [Fact] - public async Task Schema_property_for_required_to_one_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredToOne.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_to_many_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_for_required_to_many_relationship_is_not_nullable() + [Theory] + [InlineData("requiredToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -171,7 +85,7 @@ public async Task Schema_property_for_required_to_many_relationship_is_not_nulla // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs index 3af1cdc0bc..cba6e2035b 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs @@ -16,8 +16,10 @@ public RequiredTests(OpenApiTestContext(); } - [Fact] - public async Task Schema_property_for_reference_type_attribute_is_not_required() + [Theory] + [InlineData("requiredReferenceType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -25,13 +27,17 @@ public async Task Schema_property_for_reference_type_attribute_is_not_required() // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.referenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("referenceType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_required_reference_type_attribute_is_required() + [Theory] + [InlineData("referenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -39,100 +45,14 @@ public async Task Schema_property_for_required_reference_type_attribute_is_requi // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.requiredReferenceType"); - - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredReferenceType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.valueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); - }); - } - - [Fact] - public async Task Schema_property_for_required_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredValueType")); - }); - } - - [Fact] - public async Task Schema_property_for_nullable_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); - }); - } - - [Fact] - public async Task Schema_property_for_required_nullable_value_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); - - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); - }); - } - - [Fact] - public async Task Schema_property_for_to_one_relationship_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.toOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toOne")); - }); - } - - [Fact] - public async Task Schema_property_for_required_to_one_relationship_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.requiredToOne"); - - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredToOne")); - }); - } - - [Fact] - public async Task Schema_property_for_to_many_relationship_is_not_required() + [Theory] + [InlineData("requiredToOne")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -140,13 +60,16 @@ public async Task Schema_property_for_to_many_relationship_is_not_required() // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.toMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_for_required_to_many_relationship_is_not_required() + [Theory] + [InlineData("toOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -154,13 +77,13 @@ public async Task Schema_property_for_required_to_many_relationship_is_not_requi // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredToMany")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } [Fact] - public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -170,7 +93,7 @@ public async Task No_schema_properties_for_attributes_are_required_when_updating } [Fact] - public async Task No_schema_properties_for_relationships_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs index 520a9e0172..c31e4a9e42 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs @@ -21,11 +21,11 @@ public NullabilityTests( } [Theory] - [InlineData("nonNullableReferenceType")] - [InlineData("requiredNonNullableReferenceType")] - [InlineData("valueType")] - [InlineData("requiredValueType")] - public async Task Schema_property_that_describes_attribute_is_not_nullable(string jsonPropertyName) + [InlineData("nullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -35,17 +35,17 @@ public async Task Schema_property_that_describes_attribute_is_not_nullable(strin { schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } [Theory] - [InlineData("nullableReferenceType")] - [InlineData("requiredNullableReferenceType")] - [InlineData("nullableValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_that_describes_attribute_is_nullable(string jsonPropertyName) + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -55,17 +55,15 @@ public async Task Schema_property_that_describes_attribute_is_nullable(string js { schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.ShouldNotContainPath("nullable"); }); }); } [Theory] - [InlineData("nonNullableToOne")] - [InlineData("requiredNonNullableToOne")] - [InlineData("toMany")] - [InlineData("requiredToMany")] - public async Task Schema_property_that_describes_relationship_is_not_nullable(string jsonPropertyName) + [InlineData("nullableToOne")] + [InlineData("requiredNullableToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -75,15 +73,17 @@ public async Task Schema_property_that_describes_relationship_is_not_nullable(st { schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } [Theory] - [InlineData("nullableToOne")] - [InlineData("requiredNullableToOne")] - public async Task Schema_property_that_describes_relationship_is_nullable(string jsonPropertyName) + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -93,7 +93,7 @@ public async Task Schema_property_that_describes_relationship_is_nullable(string { schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs index c622636ba6..0cca901d44 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs @@ -19,11 +19,11 @@ public RequiredTests( } [Theory] - [InlineData("nonNullableReferenceType")] - [InlineData("nullableReferenceType")] - [InlineData("valueType")] - [InlineData("nullableValueType")] - public async Task Schema_property_that_describes_attribute_in_create_resource_is_not_required(string jsonPropertyName) + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -32,16 +32,16 @@ public async Task Schema_property_that_describes_attribute_in_create_resource_is document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } [Theory] - [InlineData("requiredNonNullableReferenceType")] - [InlineData("requiredNullableReferenceType")] - [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] - public async Task Schema_property_that_describes_attribute_in_create_resource_is_required(string jsonPropertyName) + [InlineData("nonNullableReferenceType")] + [InlineData("nullableReferenceType")] + [InlineData("valueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -50,7 +50,7 @@ public async Task Schema_property_that_describes_attribute_in_create_resource_is document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } @@ -58,7 +58,7 @@ public async Task Schema_property_that_describes_attribute_in_create_resource_is [InlineData("requiredNonNullableToOne")] [InlineData("requiredNullableToOne")] [InlineData("requiredToMany")] - public async Task Schema_property_that_describes_relationship_in_create_resource_is_required(string jsonPropertyName) + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -75,7 +75,7 @@ public async Task Schema_property_that_describes_relationship_in_create_resource [InlineData("nonNullableToOne")] [InlineData("nullableToOne")] [InlineData("toMany")] - public async Task Schema_property_that_describes_relationship_in_create_resource_is_not_required(string jsonPropertyName) + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -89,7 +89,17 @@ public async Task Schema_property_that_describes_relationship_in_create_resource } [Fact] - public async Task No_schema_properties_for_relationships_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_when_updating_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + } + + [Fact] + public async Task No_relationship_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs index 9a8cc9b69c..b374162562 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs @@ -18,8 +18,10 @@ public NullabilityTests(OpenApiTestContext { - schemaProperties.ShouldContainPath("nonNullableReferenceType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_non_nullable_reference_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNonNullableReferenceType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_reference_type_attribute_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nullableReferenceType").With(schemaProperty => - { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_nullable_reference_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNullableReferenceType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("valueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_value_type_attribute_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredValueType").With(schemaProperty => - { - schemaProperty.ShouldNotContainPath("nullable"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_value_type_attribute_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nullableValueType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } - [Fact] - public async Task Schema_property_that_describes_required_nullable_value_type_attribute_is_not_nullable() + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -139,15 +51,16 @@ public async Task Schema_property_that_describes_required_nullable_value_type_at // Assert document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredNullableValueType").With(schemaProperty => + schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => { schemaProperty.ShouldNotContainPath("nullable"); }); }); } - [Fact] - public async Task Schema_property_that_describes_non_nullable_to_one_relationship_is_not_nullable() + [Theory] + [InlineData("nullableToOne")] + public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -155,79 +68,20 @@ public async Task Schema_property_that_describes_non_nullable_to_one_relationshi // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("nonNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_non_nullable_to_one_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNonNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_to_one_relationship_is_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("nullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } - [Fact] - public async Task Schema_property_that_describes_required_nullable_to_one_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("requiredNullableToOne.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_to_many_relationship_is_not_nullable() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => - { - schemaProperties.ShouldContainPath("toMany.$ref").WithSchemaReferenceId(schemaReferenceId => - { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); - }); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_to_many_relationship_is_not_nullable() + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -235,7 +89,7 @@ public async Task Schema_property_that_describes_required_to_many_relationship_i // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath("requiredToMany.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); }); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs index 467d72684d..f0fbc13272 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs @@ -16,92 +16,12 @@ public RequiredTests(OpenApiTestContext(); } - [Fact] - public async Task Schema_property_that_describes_non_nullable_reference_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.nonNullableReferenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("nonNullableReferenceType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_non_nullable_reference_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredNonNullableReferenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNonNullableReferenceType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_reference_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.nullableReferenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableReferenceType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_nullable_reference_type_attribute_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredNullableReferenceType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableReferenceType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.valueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("valueType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_value_type_attribute_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => - { - attributesObjectSchema.ShouldContainPath("properties.requiredValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredValueType")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_value_type_attribute_is_not_required() + [Theory] + [InlineData("nonNullableReferenceType")] + [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] + [InlineData("requiredNullableValueType")] + public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -109,13 +29,17 @@ public async Task Schema_property_that_describes_nullable_value_type_attribute_i // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.nullableValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableValueType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_that_describes_required_nullable_value_type_attribute_is_required() + [Theory] + [InlineData("nullableReferenceType")] + [InlineData("valueType")] + [InlineData("requiredValueType")] + [InlineData("nullableValueType")] + public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -123,13 +47,16 @@ public async Task Schema_property_that_describes_required_nullable_value_type_at // Assert document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath("properties.requiredNullableValueType"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableValueType")); + attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_that_describes_non_nullable_to_one_relationship_is_required() + [Theory] + [InlineData("nonNullableToOne")] + [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] + public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -137,69 +64,16 @@ public async Task Schema_property_that_describes_non_nullable_to_one_relationshi // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.nonNullableToOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("nonNullableToOne")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); }); } - [Fact] - public async Task Schema_property_that_describes_required_non_nullable_to_one_relationship_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.requiredNonNullableToOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNonNullableToOne")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_nullable_to_one_relationship_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.nullableToOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("nullableToOne")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_nullable_to_one_relationship_is_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.requiredNullableToOne"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement("requiredNullableToOne")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_to_many_relationship_is_not_required() - { - // Act - JsonElement document = await _testContext.GetSwaggerDocumentAsync(); - - // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => - { - relationshipsObjectSchema.ShouldContainPath("properties.toMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("toMany")); - }); - } - - [Fact] - public async Task Schema_property_that_describes_required_to_many_relationship_is_not_required() + [Theory] + [InlineData("nullableToOne")] + [InlineData("toMany")] + [InlineData("requiredToMany")] + public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -207,13 +81,13 @@ public async Task Schema_property_that_describes_required_to_many_relationship_i // Assert document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath("properties.requiredToMany"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement("requiredToMany")); + relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); }); } [Fact] - public async Task No_schema_properties_for_attributes_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -223,7 +97,7 @@ public async Task No_schema_properties_for_attributes_are_required_when_updating } [Fact] - public async Task No_schema_properties_for_relationships_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_when_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); From 8304c05feb1379b3df5571b4c61f40f7211cfb1f Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 7 Jan 2023 14:39:11 +0100 Subject: [PATCH 25/66] Make AutoFakers deterministic; generate positive IDs --- test/OpenApiClientTests/FakerFactory.cs | 48 +++++++++++++++++++++-- test/TestBuildingBlocks/FakerContainer.cs | 2 +- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/test/OpenApiClientTests/FakerFactory.cs b/test/OpenApiClientTests/FakerFactory.cs index c28adbaa60..db4fe3a406 100644 --- a/test/OpenApiClientTests/FakerFactory.cs +++ b/test/OpenApiClientTests/FakerFactory.cs @@ -1,5 +1,7 @@ using System.Reflection; using AutoBogus; +using JetBrains.Annotations; +using TestBuildingBlocks; namespace OpenApiClientTests; @@ -14,18 +16,27 @@ private FakerFactory() public AutoFaker Create() where TTarget : class { - return new AutoFaker(); + return GetDeterministicFaker(); + } + + private static AutoFaker GetDeterministicFaker() + where TTarget : class + { + var autoFaker = new AutoFaker(); + autoFaker.UseSeed(FakerContainer.GetFakerSeed()); + return autoFaker; } public AutoFaker CreateForObjectWithResourceId() where TTarget : class { - return new AutoFaker().Configure(builder => builder.WithOverride(new ResourceStringIdOverride())); + return GetDeterministicFaker().Configure(builder => builder.WithOverride(new ResourceStringIdOverride())); } private sealed class ResourceStringIdOverride : AutoGeneratorOverride { - private readonly IAutoFaker _idFaker = AutoFaker.Create(); + // AutoFaker has a class constraint, while TId has not, so we need to wrap it. + private readonly AutoFaker> _idContainerFaker = GetDeterministicFaker>(); public override bool CanOverride(AutoGenerateContext context) { @@ -35,7 +46,36 @@ public override bool CanOverride(AutoGenerateContext context) public override void Generate(AutoGenerateOverrideContext context) { - ((dynamic)context.Instance).Id = _idFaker.Generate()!.ToString()!; + object idValue = _idContainerFaker.Generate().Value!; + idValue = ToPositiveValue(idValue); + + ((dynamic)context.Instance).Id = idValue.ToString()!; + } + + private static object ToPositiveValue(object idValue) + { + if (idValue is short shortValue) + { + return Math.Abs(shortValue); + } + + if (idValue is int intValue) + { + return Math.Abs(intValue); + } + + if (idValue is long longValue) + { + return Math.Abs(longValue); + } + + return idValue; + } + + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + private sealed class ObjectContainer + { + public TValue? Value { get; set; } } } } diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs index 99cce6e04c..d9cc96e0b3 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -15,7 +15,7 @@ static FakerContainer() Date.SystemClock = () => 1.January(2020).AsUtc(); } - protected static int GetFakerSeed() + public static int GetFakerSeed() { // The goal here is to have stable data over multiple test runs, but at the same time different data per test case. From 9672b143941a476d17acf56bcfc926489ca72d16 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 7 Jan 2023 20:59:03 +0100 Subject: [PATCH 26/66] Fix nameof --- src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index f119396747..99bfe7d3b6 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -40,8 +40,8 @@ public IDisposable WithPartialAttributeSerialization article.Title'."); + throw new ArgumentException($"The expression '{selector}' should select a single property. For example: 'article => article.Title'.", + nameof(alwaysIncludedAttributeSelectors)); } } From 345a68942556358038914854d2d6673a82187219 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 7 Jan 2023 21:46:22 +0100 Subject: [PATCH 27/66] Use On/Off naming, shorten type names by using Nrt+Msv --- .../OpenApiClientTests.csproj | 18 ++++++++-------- .../CreateResourceTests.cs | 6 +++--- .../GeneratedCode/NrtOffMsvOffClient.cs | 2 +- .../NrtOffMsvOffFakers.cs} | 6 +++--- .../NullabilityTests.cs | 4 ++-- .../UpdateResourceTests.cs | 6 +++--- .../ModelStateValidationOff}/swagger.g.json | 0 .../CreateResourceTests.cs | 6 +++--- .../GeneratedCode/NrtOffMsvOnClient.cs | 2 +- .../NrtOffMsvOnFakers.cs} | 6 +++--- .../NullabilityTests.cs | 4 ++-- .../UpdateResourceTests.cs | 6 +++--- .../ModelStateValidationOn}/swagger.g.json | 0 .../CreateResourceTests.cs | 6 +++--- .../GeneratedCode/NrtOnMsvOffClient.cs | 2 +- .../NrtOnMsvOffFakers.cs} | 6 +++--- .../NullabilityTests.cs | 4 ++-- .../UpdateResourceTests.cs | 6 +++--- .../ModelStateValidationOff}/swagger.g.json | 0 .../CreateResourceTests.cs | 6 +++--- .../GeneratedCode/NrtOnMsvOnClient.cs | 2 +- .../NrtOnMsvOnFakers.cs} | 6 +++--- .../NullabilityTests.cs | 4 ++-- .../UpdateResourceTests.cs | 6 +++--- .../ModelStateValidationOn}/swagger.g.json | 0 ...ionDisabledStartup.cs => MsvOffStartup.cs} | 2 +- .../NullabilityTests.cs | 19 ++++++++--------- .../ModelStateValidationOff}/RequiredTests.cs | 15 +++++++------ .../NullabilityTests.cs | 21 ++++++++----------- .../ModelStateValidationOn}/RequiredTests.cs | 17 +++++++-------- .../NrtOffDbContext.cs} | 16 +++++++------- .../NrtOffResource.cs} | 4 ++-- .../NullabilityTests.cs | 19 ++++++++--------- .../ModelStateValidationOff}/RequiredTests.cs | 19 ++++++++--------- .../NullabilityTests.cs | 21 ++++++++----------- .../ModelStateValidationOn}/RequiredTests.cs | 21 ++++++++----------- .../NrtOnDbContext.cs} | 20 +++++++++--------- .../NrtOnResource.cs} | 4 ++-- 38 files changed, 148 insertions(+), 164 deletions(-) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOff}/CreateResourceTests.cs (99%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOff}/GeneratedCode/NrtOffMsvOffClient.cs (85%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs => NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs} (88%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOff}/NullabilityTests.cs (91%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOff}/UpdateResourceTests.cs (97%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOff}/swagger.g.json (100%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOn}/CreateResourceTests.cs (99%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOn}/GeneratedCode/NrtOffMsvOnClient.cs (85%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs => NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs} (90%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOn}/NullabilityTests.cs (91%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOn}/UpdateResourceTests.cs (97%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOn}/swagger.g.json (100%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOff}/CreateResourceTests.cs (99%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOff}/GeneratedCode/NrtOnMsvOffClient.cs (85%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs => NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs} (90%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOff}/NullabilityTests.cs (93%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOff}/UpdateResourceTests.cs (97%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOff}/swagger.g.json (100%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOn}/CreateResourceTests.cs (99%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOn}/GeneratedCode/NrtOnMsvOnClient.cs (86%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs => NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs} (90%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOn}/NullabilityTests.cs (93%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOn}/UpdateResourceTests.cs (97%) rename test/OpenApiClientTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOn}/swagger.g.json (100%) rename test/OpenApiTests/ResourceFieldValidation/{ModelStateValidationDisabledStartup.cs => MsvOffStartup.cs} (81%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOff}/NullabilityTests.cs (82%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationEnabled => NullableReferenceTypesOff/ModelStateValidationOff}/RequiredTests.cs (86%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOn}/NullabilityTests.cs (80%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/ModelStateValidationDisabled => NullableReferenceTypesOff/ModelStateValidationOn}/RequiredTests.cs (84%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs => NullableReferenceTypesOff/NrtOffDbContext.cs} (56%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesDisabled/NrtDisabledResource.cs => NullableReferenceTypesOff/NrtOffResource.cs} (93%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOff}/NullabilityTests.cs (83%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationEnabled => NullableReferenceTypesOn/ModelStateValidationOff}/RequiredTests.cs (87%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOn}/NullabilityTests.cs (81%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/ModelStateValidationDisabled => NullableReferenceTypesOn/ModelStateValidationOn}/RequiredTests.cs (85%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs => NullableReferenceTypesOn/NrtOnDbContext.cs} (57%) rename test/OpenApiTests/ResourceFieldValidation/{NullableReferenceTypesEnabled/NrtEnabledResource.cs => NullableReferenceTypesOn/NrtOnResource.cs} (95%) diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index c5e7f0acdd..942b8929bf 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -1,4 +1,4 @@ - + $(TargetFrameworkName) @@ -53,32 +53,32 @@ NSwagCSharp /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions - + NrtOffMsvOffClient NrtOffMsvOffClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - + NrtOffMsvOnClient NrtOffMsvOnClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:false - + NrtOnMsvOffClient NrtOnMsvOffClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - + NrtOnMsvOnClient NrtOnMsvOnClient.cs NSwagCSharp - OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode + OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs similarity index 99% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 5dfa347b05..e88561466d 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; public sealed class CreateResourceTests : OpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOffFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs index 4bea9db090..f3ee9a9c53 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/GeneratedCode/NrtOffMsvOffClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedCode/NrtOffMsvOffClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; internal partial class NrtOffMsvOffClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs similarity index 88% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs index 817344c11c..0338dd64a3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOffMsvOffFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs similarity index 91% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs index 8079c4896c..5ce66e51d2 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs similarity index 97% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 68e71e97b9..c5309246e8 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -3,15 +3,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; public sealed class UpdateResourceTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOffFakers _fakers = new(); [Fact] public async Task Cannot_exclude_id() diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs similarity index 99% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index f3a5130486..7ad0f848d5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; public sealed class CreateResourceTests : OpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOnFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs index 94bb5efc37..a722c5d49b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/GeneratedCode/NrtOffMsvOnClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedCode/NrtOffMsvOnClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; internal partial class NrtOffMsvOnClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs similarity index 90% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs index c032f0c073..69091f9d9b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOffMsvOnFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs similarity index 91% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs index 53252ee439..5ef7d62faf 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs similarity index 97% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 81f54f6dd0..c5e0aee40d 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -3,15 +3,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; public sealed class UpdateResourceTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOffMsvOnFakers _fakers = new(); [Fact] public async Task Cannot_exclude_id() diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs similarity index 99% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 0ca3a6fb82..1f74ce0cf7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; public sealed class CreateResourceTests : OpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOffFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs similarity index 85% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs index c44cdbe3d7..77b0854984 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/GeneratedCode/NrtOnMsvOffClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedCode/NrtOnMsvOffClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; internal partial class NrtOnMsvOffClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs similarity index 90% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs index b0c9c260c7..a4ff6d1057 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOnMsvOffFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs similarity index 93% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs index dc2bc84ccd..c094beef16 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs similarity index 97% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 5596c17962..cef728c02b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -3,15 +3,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; public sealed class UpdateResourceTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOffFakers _fakers = new(); [Fact] public async Task Cannot_exclude_id() diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs similarity index 99% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 44aa5da920..5d86403aa4 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -4,15 +4,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; public sealed class CreateResourceTests : OpenApiClientTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOnFakers _fakers = new(); [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs similarity index 86% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs index 1eed829acb..6679b1c168 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/GeneratedCode/NrtOnMsvOnClient.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedCode/NrtOnMsvOnClient.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; internal partial class NrtOnMsvOnClient : JsonApiClient { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs similarity index 90% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs index e3dbbd0d36..fbad50929e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/ResourceFieldValidationFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs @@ -1,9 +1,9 @@ using Bogus; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -internal sealed class ResourceFieldValidationFakers +internal sealed class NrtOnMsvOnFakers { private readonly Lazy> _lazyPostAttributesFaker = new(() => FakerFactory.Instance.Create()); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs similarity index 93% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs index e3c1a5ce31..7774891729 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -1,9 +1,9 @@ using System.Reflection; using FluentAssertions; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; public sealed class NullabilityTests { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs similarity index 97% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index d0695d6cf7..b5a3c49e6f 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -3,15 +3,15 @@ using FluentAssertions; using FluentAssertions.Specialized; using Newtonsoft.Json; -using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled.GeneratedCode; +using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; using Xunit; -namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; public sealed class UpdateResourceTests { - private readonly ResourceFieldValidationFakers _fakers = new(); + private readonly NrtOnMsvOnFakers _fakers = new(); [Fact] public async Task Cannot_exclude_id() diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json similarity index 100% rename from test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/swagger.g.json rename to test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json diff --git a/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs b/test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs similarity index 81% rename from test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs rename to test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs index 0bea8b4d58..838803f9c4 100644 --- a/test/OpenApiTests/ResourceFieldValidation/ModelStateValidationDisabledStartup.cs +++ b/test/OpenApiTests/ResourceFieldValidation/MsvOffStartup.cs @@ -5,7 +5,7 @@ namespace OpenApiTests.ResourceFieldValidation; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class ModelStateValidationDisabledStartup : OpenApiStartup +public sealed class MsvOffStartup : OpenApiStartup where TDbContext : TestableDbContext { protected override void SetJsonApiOptions(JsonApiOptions options) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs similarity index 82% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs index dcbcc572ab..1fc5394d5c 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -3,24 +3,25 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> +public sealed class NullabilityTests : IClassFixture, NrtOffDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NrtOffDbContext> testContext) { _testContext = testContext; - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled"; + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff"; } [Theory] [InlineData("referenceType")] + [InlineData("requiredReferenceType")] [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act @@ -37,10 +38,8 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN } [Theory] - [InlineData("requiredReferenceType")] [InlineData("valueType")] [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act @@ -58,6 +57,7 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope [Theory] [InlineData("toOne")] + [InlineData("requiredToOne")] public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act @@ -74,7 +74,6 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper } [Theory] - [InlineData("requiredToOne")] [InlineData("toMany")] [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs similarity index 86% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs index cba6e2035b..78d67b97c6 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationEnabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs @@ -2,22 +2,22 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> +public sealed class RequiredTests : IClassFixture, NrtOffDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> _testContext; + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; - public RequiredTests(OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + public RequiredTests(OpenApiTestContext, NrtOffDbContext> testContext) { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] [InlineData("requiredReferenceType")] + [InlineData("requiredValueType")] [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { @@ -35,7 +35,6 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc [Theory] [InlineData("referenceType")] [InlineData("valueType")] - [InlineData("requiredValueType")] [InlineData("nullableValueType")] public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { @@ -52,6 +51,7 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res [Theory] [InlineData("requiredToOne")] + [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act @@ -68,7 +68,6 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso [Theory] [InlineData("toOne")] [InlineData("toMany")] - [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs similarity index 80% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs index 40c6c5558a..410c9d2098 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -3,28 +3,23 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> +public sealed class NullabilityTests : IClassFixture, NrtOffDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NrtOffDbContext> testContext) { _testContext = testContext; - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled"; + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn"; } [Theory] [InlineData("referenceType")] - [InlineData("requiredReferenceType")] [InlineData("nullableValueType")] - [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act @@ -41,8 +36,10 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN } [Theory] + [InlineData("requiredReferenceType")] [InlineData("valueType")] [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act @@ -60,7 +57,6 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope [Theory] [InlineData("toOne")] - [InlineData("requiredToOne")] public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act @@ -77,6 +73,7 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper } [Theory] + [InlineData("requiredToOne")] [InlineData("toMany")] [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs similarity index 84% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs index 5688462fb9..dc343a3da0 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/ModelStateValidationDisabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs @@ -2,25 +2,21 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled.ModelStateValidationDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesDisabledDbContext>> +public sealed class RequiredTests : IClassFixture, NrtOffDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesDisabledDbContext> - _testContext; + private readonly OpenApiTestContext, NrtOffDbContext> _testContext; - public RequiredTests( - OpenApiTestContext, NullableReferenceTypesDisabledDbContext> testContext) + public RequiredTests(OpenApiTestContext, NrtOffDbContext> testContext) { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] [InlineData("requiredReferenceType")] - [InlineData("requiredValueType")] [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { @@ -38,6 +34,7 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc [Theory] [InlineData("referenceType")] [InlineData("valueType")] + [InlineData("requiredValueType")] [InlineData("nullableValueType")] public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { @@ -54,7 +51,6 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res [Theory] [InlineData("requiredToOne")] - [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act @@ -71,6 +67,7 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso [Theory] [InlineData("toOne")] [InlineData("toMany")] + [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs similarity index 56% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs index 1e8691d9de..f70e8e7f7a 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NullableReferenceTypesDisabledDbContext.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs @@ -2,32 +2,32 @@ using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; // @formatter:wrap_chained_method_calls chop_always [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesDisabledDbContext : TestableDbContext +public sealed class NrtOffDbContext : TestableDbContext { - public DbSet NrtDisabledResources => Set(); + public DbSet NrtOffResources => Set(); - public NullableReferenceTypesDisabledDbContext(DbContextOptions options) + public NrtOffDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { - builder.Entity() + builder.Entity() .HasOne(resource => resource.ToOne); - builder.Entity() + builder.Entity() .HasOne(resource => resource.RequiredToOne); - builder.Entity() + builder.Entity() .HasMany(resource => resource.ToMany); - builder.Entity() + builder.Entity() .HasMany(resource => resource.RequiredToMany); base.OnModelCreating(builder); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs similarity index 93% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs index 4a297cf3e9..99ef7304b9 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesDisabled/NrtDisabledResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs @@ -5,11 +5,11 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] -public sealed class NrtDisabledResource : Identifiable +public sealed class NrtOffResource : Identifiable { [Attr] public string ReferenceType { get; set; } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs similarity index 83% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs index b374162562..c51bef61ce 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -3,24 +3,25 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> +public sealed class NullabilityTests : IClassFixture, NrtOnDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; - public NullabilityTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NrtOnDbContext> testContext) { _testContext = testContext; - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled"; + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff"; } [Theory] [InlineData("nullableReferenceType")] + [InlineData("requiredNullableReferenceType")] [InlineData("nullableValueType")] + [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act @@ -39,10 +40,8 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN [Theory] [InlineData("nonNullableReferenceType")] [InlineData("requiredNonNullableReferenceType")] - [InlineData("requiredNullableReferenceType")] [InlineData("valueType")] [InlineData("requiredValueType")] - [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act @@ -60,6 +59,7 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope [Theory] [InlineData("nullableToOne")] + [InlineData("requiredNullableToOne")] public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act @@ -78,7 +78,6 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper [Theory] [InlineData("nonNullableToOne")] [InlineData("requiredNonNullableToOne")] - [InlineData("requiredNullableToOne")] [InlineData("toMany")] [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs similarity index 87% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs index f0fbc13272..34f7da02fe 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationEnabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs @@ -2,24 +2,23 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> +public sealed class RequiredTests : IClassFixture, NrtOnDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> _testContext; + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; - public RequiredTests(OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public RequiredTests(OpenApiTestContext, NrtOnDbContext> testContext) { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] - [InlineData("nonNullableReferenceType")] [InlineData("requiredNonNullableReferenceType")] [InlineData("requiredNullableReferenceType")] + [InlineData("requiredValueType")] [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { @@ -35,9 +34,9 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc } [Theory] + [InlineData("nonNullableReferenceType")] [InlineData("nullableReferenceType")] [InlineData("valueType")] - [InlineData("requiredValueType")] [InlineData("nullableValueType")] public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { @@ -53,9 +52,9 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res } [Theory] - [InlineData("nonNullableToOne")] [InlineData("requiredNonNullableToOne")] [InlineData("requiredNullableToOne")] + [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act @@ -70,9 +69,9 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso } [Theory] + [InlineData("nonNullableToOne")] [InlineData("nullableToOne")] [InlineData("toMany")] - [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs similarity index 81% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs index c31e4a9e42..fb3c71a915 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -3,28 +3,23 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class NullabilityTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> +public sealed class NullabilityTests : IClassFixture, NrtOnDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; - public NullabilityTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public NullabilityTests(OpenApiTestContext, NrtOnDbContext> testContext) { _testContext = testContext; - testContext.UseController(); - testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled"; + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn"; } [Theory] [InlineData("nullableReferenceType")] - [InlineData("requiredNullableReferenceType")] [InlineData("nullableValueType")] - [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyName) { // Act @@ -43,8 +38,10 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN [Theory] [InlineData("nonNullableReferenceType")] [InlineData("requiredNonNullableReferenceType")] + [InlineData("requiredNullableReferenceType")] [InlineData("valueType")] [InlineData("requiredValueType")] + [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_not_nullable(string jsonPropertyName) { // Act @@ -62,7 +59,6 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope [Theory] [InlineData("nullableToOne")] - [InlineData("requiredNullableToOne")] public async Task Schema_property_for_relationship_is_nullable(string jsonPropertyName) { // Act @@ -81,6 +77,7 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper [Theory] [InlineData("nonNullableToOne")] [InlineData("requiredNonNullableToOne")] + [InlineData("requiredNullableToOne")] [InlineData("toMany")] [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_nullable(string jsonPropertyName) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs similarity index 85% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs index 0cca901d44..af21ca967f 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/ModelStateValidationDisabled/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs @@ -2,26 +2,23 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled.ModelStateValidationDisabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class RequiredTests - : IClassFixture, NullableReferenceTypesEnabledDbContext>> +public sealed class RequiredTests : IClassFixture, NrtOnDbContext>> { - private readonly OpenApiTestContext, NullableReferenceTypesEnabledDbContext> - _testContext; + private readonly OpenApiTestContext, NrtOnDbContext> _testContext; - public RequiredTests( - OpenApiTestContext, NullableReferenceTypesEnabledDbContext> testContext) + public RequiredTests(OpenApiTestContext, NrtOnDbContext> testContext) { _testContext = testContext; - testContext.UseController(); + testContext.UseController(); } [Theory] + [InlineData("nonNullableReferenceType")] [InlineData("requiredNonNullableReferenceType")] [InlineData("requiredNullableReferenceType")] - [InlineData("requiredValueType")] [InlineData("requiredNullableValueType")] public async Task Schema_property_for_attribute_is_required_for_creating_resource(string jsonPropertyName) { @@ -37,9 +34,9 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc } [Theory] - [InlineData("nonNullableReferenceType")] [InlineData("nullableReferenceType")] [InlineData("valueType")] + [InlineData("requiredValueType")] [InlineData("nullableValueType")] public async Task Schema_property_for_attribute_is_not_required_for_creating_resource(string jsonPropertyName) { @@ -55,9 +52,9 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res } [Theory] + [InlineData("nonNullableToOne")] [InlineData("requiredNonNullableToOne")] [InlineData("requiredNullableToOne")] - [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_required_for_creating_resource(string jsonPropertyName) { // Act @@ -72,9 +69,9 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso } [Theory] - [InlineData("nonNullableToOne")] [InlineData("nullableToOne")] [InlineData("toMany")] + [InlineData("requiredToMany")] public async Task Schema_property_for_relationship_is_not_required_for_creating_resource(string jsonPropertyName) { // Act diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs similarity index 57% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs index 54f1620db5..de405972a5 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NullableReferenceTypesEnabledDbContext.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs @@ -2,38 +2,38 @@ using Microsoft.EntityFrameworkCore; using TestBuildingBlocks; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; // @formatter:wrap_chained_method_calls chop_always [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class NullableReferenceTypesEnabledDbContext : TestableDbContext +public sealed class NrtOnDbContext : TestableDbContext { - public DbSet NullableReferenceTypesEnabledResources => Set(); + public DbSet NrtOnResources => Set(); - public NullableReferenceTypesEnabledDbContext(DbContextOptions options) + public NrtOnDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { - builder.Entity() + builder.Entity() .HasOne(resource => resource.NonNullableToOne); - builder.Entity() + builder.Entity() .HasOne(resource => resource.RequiredNonNullableToOne); - builder.Entity() + builder.Entity() .HasOne(resource => resource.NullableToOne); - builder.Entity() + builder.Entity() .HasOne(resource => resource.RequiredNullableToOne); - builder.Entity() + builder.Entity() .HasMany(resource => resource.ToMany); - builder.Entity() + builder.Entity() .HasMany(resource => resource.RequiredToMany); base.OnModelCreating(builder); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs similarity index 95% rename from test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs rename to test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs index 8908f2a50f..3228bd126e 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesEnabled/NrtEnabledResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs @@ -3,11 +3,11 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesEnabled; +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] -public sealed class NrtEnabledResource : Identifiable +public sealed class NrtOnResource : Identifiable { [Attr] public string NonNullableReferenceType { get; set; } = null!; From 22ad3008643ec17aee0ebdb745b17d44cddfec4f Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 7 Jan 2023 22:11:45 +0100 Subject: [PATCH 28/66] Renamed EmptyResource to Empty to further shorten FK names --- .../OpenApiClientTests.csproj | 3 +- .../NrtOffMsvOffFakers.cs | 12 +- .../ModelStateValidationOff/swagger.g.json | 116 ++++++------- .../NrtOffMsvOnFakers.cs | 18 +- .../ModelStateValidationOn/swagger.g.json | 132 +++++++------- .../NrtOnMsvOffFakers.cs | 18 +- .../ModelStateValidationOff/swagger.g.json | 164 +++++++++--------- .../NrtOnMsvOnFakers.cs | 18 +- .../ModelStateValidationOn/swagger.g.json | 164 +++++++++--------- .../{EmptyResource.cs => Empty.cs} | 2 +- .../NrtOffResource.cs | 8 +- .../NullableReferenceTypesOn/NrtOnResource.cs | 12 +- 12 files changed, 333 insertions(+), 334 deletions(-) rename test/OpenApiTests/ResourceFieldValidation/{EmptyResource.cs => Empty.cs} (80%) diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 942b8929bf..0052d618eb 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -1,4 +1,4 @@ - + $(TargetFrameworkName) @@ -82,5 +82,4 @@ /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true - diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs index 0338dd64a3..adb0e97833 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NrtOffMsvOffFakers.cs @@ -11,14 +11,14 @@ internal sealed class NrtOffMsvOffFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json index 572f1c7615..72c37b76bd 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -366,7 +366,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -397,7 +397,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -432,7 +432,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -461,7 +461,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -492,7 +492,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -548,7 +548,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -583,7 +583,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -612,7 +612,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -643,7 +643,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -699,7 +699,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -730,7 +730,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -796,7 +796,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -825,7 +825,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -856,7 +856,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -885,7 +885,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -927,7 +927,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -937,7 +937,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -953,7 +953,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -962,7 +962,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -978,7 +978,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -986,7 +986,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -995,7 +995,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1005,7 +1005,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1021,9 +1021,9 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, @@ -1208,7 +1208,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1218,7 +1218,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1238,7 +1238,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1248,7 +1248,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1268,7 +1268,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1277,7 +1277,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1287,7 +1287,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1296,7 +1296,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1571,16 +1571,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1593,16 +1593,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1615,16 +1615,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false @@ -1635,7 +1635,7 @@ ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -1644,13 +1644,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -1659,7 +1659,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs index 69091f9d9b..14d23cb247 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NrtOffMsvOnFakers.cs @@ -11,18 +11,18 @@ internal sealed class NrtOffMsvOnFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json index f366cc19e2..e861e2a99f 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -366,7 +366,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -397,7 +397,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -432,7 +432,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -461,7 +461,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -492,7 +492,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -548,7 +548,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -583,7 +583,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -612,7 +612,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -643,7 +643,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -699,7 +699,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -730,7 +730,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -796,7 +796,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -825,7 +825,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -856,7 +856,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -885,7 +885,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -927,7 +927,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -937,7 +937,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -953,7 +953,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -962,7 +962,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -978,7 +978,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -986,7 +986,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -995,7 +995,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1005,7 +1005,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1021,7 +1021,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1029,7 +1029,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1044,13 +1044,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1058,7 +1058,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1254,7 +1254,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1264,7 +1264,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1284,7 +1284,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1294,7 +1294,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1314,7 +1314,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1323,7 +1323,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1333,7 +1333,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1342,7 +1342,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1609,16 +1609,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1630,16 +1630,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1651,16 +1651,16 @@ "type": "object", "properties": { "toOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false @@ -1671,7 +1671,7 @@ ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -1680,13 +1680,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -1695,7 +1695,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -1708,26 +1708,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs index a4ff6d1057..4b6365764e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NrtOnMsvOffFakers.cs @@ -11,18 +11,18 @@ internal sealed class NrtOnMsvOffFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json index 8619a357c8..52d81ecec1 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -370,7 +370,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -399,7 +399,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -430,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -459,7 +459,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -486,7 +486,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -550,7 +550,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -581,7 +581,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -610,7 +610,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -637,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -701,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -732,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -788,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -823,7 +823,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -852,7 +852,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -883,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -939,7 +939,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -970,7 +970,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1001,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1036,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1065,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1096,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1125,7 +1125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1152,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1183,7 +1183,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1214,7 +1214,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1229,7 +1229,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -1239,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -1255,7 +1255,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -1264,7 +1264,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1280,7 +1280,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -1288,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1297,7 +1297,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1307,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1323,7 +1323,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1331,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1346,13 +1346,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1360,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1556,7 +1556,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1566,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1586,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1596,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1616,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1625,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1635,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1644,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1942,22 +1942,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1971,22 +1971,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -2000,22 +2000,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false @@ -2026,7 +2026,7 @@ ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -2035,13 +2035,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -2050,7 +2050,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -2063,26 +2063,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs index fbad50929e..9906627046 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NrtOnMsvOnFakers.cs @@ -11,18 +11,18 @@ internal sealed class NrtOnMsvOnFakers private readonly Lazy> _lazyPatchAttributesFaker = new(() => FakerFactory.Instance.Create()); - private readonly Lazy> _lazyToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyNullableToOneFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyNullableToOneFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); - private readonly Lazy> _lazyToManyFaker = new(() => - FakerFactory.Instance.CreateForObjectWithResourceId()); + private readonly Lazy> _lazyToManyFaker = new(() => + FakerFactory.Instance.CreateForObjectWithResourceId()); public Faker PostAttributes => _lazyPostAttributesFaker.Value; public Faker PatchAttributes => _lazyPatchAttributesFaker.Value; - public Faker ToOne => _lazyToOneFaker.Value; - public Faker NullableToOne => _lazyNullableToOneFaker.Value; - public Faker ToMany => _lazyToManyFaker.Value; + public Faker ToOne => _lazyToOneFaker.Value; + public Faker NullableToOne => _lazyNullableToOneFaker.Value; + public Faker ToMany => _lazyToManyFaker.Value; } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json index 897983660d..61ae55a60b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json @@ -219,7 +219,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -248,7 +248,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -279,7 +279,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -308,7 +308,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -335,7 +335,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -370,7 +370,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -399,7 +399,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/nullableEmptySecondaryResponseDocument" } } } @@ -430,7 +430,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -459,7 +459,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableEmptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/nullableEmptyIdentifierResponseDocument" } } } @@ -486,7 +486,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" } } } @@ -521,7 +521,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -550,7 +550,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -581,7 +581,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -610,7 +610,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -637,7 +637,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -672,7 +672,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -701,7 +701,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceSecondaryResponseDocument" + "$ref": "#/components/schemas/emptySecondaryResponseDocument" } } } @@ -732,7 +732,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -761,7 +761,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierResponseDocument" } } } @@ -788,7 +788,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" } } } @@ -823,7 +823,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -852,7 +852,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -883,7 +883,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -912,7 +912,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -939,7 +939,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -970,7 +970,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1001,7 +1001,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1036,7 +1036,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1065,7 +1065,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceCollectionResponseDocument" + "$ref": "#/components/schemas/emptyCollectionResponseDocument" } } } @@ -1096,7 +1096,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1125,7 +1125,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/emptyResourceIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/emptyIdentifierCollectionResponseDocument" } } } @@ -1152,7 +1152,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1183,7 +1183,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1214,7 +1214,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } } } @@ -1229,7 +1229,7 @@ }, "components": { "schemas": { - "emptyResourceCollectionResponseDocument": { + "emptyCollectionResponseDocument": { "required": [ "data", "links" @@ -1239,7 +1239,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" } }, "meta": { @@ -1255,7 +1255,7 @@ }, "additionalProperties": false }, - "emptyResourceDataInResponse": { + "emptyDataInResponse": { "required": [ "id", "links", @@ -1264,7 +1264,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1280,7 +1280,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifier": { + "emptyIdentifier": { "required": [ "id", "type" @@ -1288,7 +1288,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/emptyResourceResourceType" + "$ref": "#/components/schemas/emptyResourceType" }, "id": { "minLength": 1, @@ -1297,7 +1297,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierCollectionResponseDocument": { + "emptyIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1307,7 +1307,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "meta": { @@ -1323,7 +1323,7 @@ }, "additionalProperties": false }, - "emptyResourceIdentifierResponseDocument": { + "emptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1331,7 +1331,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "meta": { "type": "object", @@ -1346,13 +1346,13 @@ }, "additionalProperties": false }, - "emptyResourceResourceType": { + "emptyResourceType": { "enum": [ - "emptyResources" + "empties" ], "type": "string" }, - "emptyResourceSecondaryResponseDocument": { + "emptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1360,7 +1360,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, "meta": { "type": "object", @@ -1556,7 +1556,7 @@ }, "nullable": true }, - "nullableEmptyResourceIdentifierResponseDocument": { + "nullableEmptyIdentifierResponseDocument": { "required": [ "data", "links" @@ -1566,7 +1566,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1586,7 +1586,7 @@ }, "additionalProperties": false }, - "nullableEmptyResourceSecondaryResponseDocument": { + "nullableEmptySecondaryResponseDocument": { "required": [ "data", "links" @@ -1596,7 +1596,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceDataInResponse" + "$ref": "#/components/schemas/emptyDataInResponse" }, { "$ref": "#/components/schemas/nullValue" @@ -1616,7 +1616,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInRequest": { + "nullableToOneEmptyInRequest": { "required": [ "data" ], @@ -1625,7 +1625,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1635,7 +1635,7 @@ }, "additionalProperties": false }, - "nullableToOneEmptyResourceInResponse": { + "nullableToOneEmptyInResponse": { "required": [ "links" ], @@ -1644,7 +1644,7 @@ "data": { "oneOf": [ { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, { "$ref": "#/components/schemas/nullValue" @@ -1936,22 +1936,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1965,22 +1965,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInRequest" + "$ref": "#/components/schemas/nullableToOneEmptyInRequest" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInRequest" + "$ref": "#/components/schemas/toOneEmptyInRequest" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInRequest" + "$ref": "#/components/schemas/toManyEmptyInRequest" } }, "additionalProperties": false @@ -1994,22 +1994,22 @@ "type": "object", "properties": { "nonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "requiredNonNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "nullableToOne": { - "$ref": "#/components/schemas/nullableToOneEmptyResourceInResponse" + "$ref": "#/components/schemas/nullableToOneEmptyInResponse" }, "requiredNullableToOne": { - "$ref": "#/components/schemas/toOneEmptyResourceInResponse" + "$ref": "#/components/schemas/toOneEmptyInResponse" }, "toMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" }, "requiredToMany": { - "$ref": "#/components/schemas/toManyEmptyResourceInResponse" + "$ref": "#/components/schemas/toManyEmptyInResponse" } }, "additionalProperties": false @@ -2020,7 +2020,7 @@ ], "type": "string" }, - "toManyEmptyResourceInRequest": { + "toManyEmptyInRequest": { "required": [ "data" ], @@ -2029,13 +2029,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } } }, "additionalProperties": false }, - "toManyEmptyResourceInResponse": { + "toManyEmptyInResponse": { "required": [ "links" ], @@ -2044,7 +2044,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "links": { @@ -2057,26 +2057,26 @@ }, "additionalProperties": false }, - "toOneEmptyResourceInRequest": { + "toOneEmptyInRequest": { "required": [ "data" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" } }, "additionalProperties": false }, - "toOneEmptyResourceInResponse": { + "toOneEmptyInResponse": { "required": [ "links" ], "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/emptyResourceIdentifier" + "$ref": "#/components/schemas/emptyIdentifier" }, "links": { "$ref": "#/components/schemas/linksInRelationshipObject" diff --git a/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs b/test/OpenApiTests/ResourceFieldValidation/Empty.cs similarity index 80% rename from test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs rename to test/OpenApiTests/ResourceFieldValidation/Empty.cs index 0d7ddf5f1c..150c00dcb0 100644 --- a/test/OpenApiTests/ResourceFieldValidation/EmptyResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/Empty.cs @@ -4,6 +4,6 @@ namespace OpenApiTests.ResourceFieldValidation; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class EmptyResource : Identifiable +public class Empty : Identifiable { } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs index 99ef7304b9..bbc028e155 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs @@ -33,16 +33,16 @@ public sealed class NrtOffResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public EmptyResource ToOne { get; set; } + public Empty ToOne { get; set; } [Required] [HasOne] - public EmptyResource RequiredToOne { get; set; } + public Empty RequiredToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs index 3228bd126e..14ad004dc9 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs @@ -38,23 +38,23 @@ public sealed class NrtOnResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public EmptyResource NonNullableToOne { get; set; } = null!; + public Empty NonNullableToOne { get; set; } = null!; [Required] [HasOne] - public EmptyResource RequiredNonNullableToOne { get; set; } = null!; + public Empty RequiredNonNullableToOne { get; set; } = null!; [HasOne] - public EmptyResource? NullableToOne { get; set; } + public Empty? NullableToOne { get; set; } [Required] [HasOne] - public EmptyResource? RequiredNullableToOne { get; set; } + public Empty? RequiredNullableToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } From 3069533f6d9757e4f397e169bae5b8fd270cd371 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 00:24:44 +0100 Subject: [PATCH 29/66] Fixed invalid EF Core mappings, resulting in logged warnings and inability to clear optional to-one relationship when NRT off; fixed wrong public names --- .../ModelStateValidationOff/swagger.g.json | 84 ++++++------- .../ModelStateValidationOn/swagger.g.json | 84 ++++++------- .../ModelStateValidationOff/swagger.g.json | 112 +++++++++--------- .../ModelStateValidationOn/swagger.g.json | 112 +++++++++--------- .../ResourceFieldValidation/Empty.cs | 9 -- .../NrtOffDbContext.cs | 19 ++- .../NullableReferenceTypesOff/NrtOffEmpty.cs | 13 ++ .../NrtOffResource.cs | 10 +- .../NrtOnDbContext.cs | 27 +++-- .../NullableReferenceTypesOn/NrtOnEmpty.cs | 11 ++ .../NullableReferenceTypesOn/NrtOnResource.cs | 14 +-- 11 files changed, 266 insertions(+), 229 deletions(-) delete mode 100644 test/OpenApiTests/ResourceFieldValidation/Empty.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs create mode 100644 test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json index 72c37b76bd..c0b2808012 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -317,7 +317,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -348,7 +348,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -379,7 +379,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -409,10 +409,10 @@ } } }, - "/Resource/{id}/requiredToOne": { + "/resources/{id}/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOne", "parameters": [ @@ -441,7 +441,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOne", "parameters": [ @@ -469,10 +469,10 @@ } } }, - "/Resource/{id}/relationships/requiredToOne": { + "/resources/{id}/relationships/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOneRelationship", "parameters": [ @@ -501,7 +501,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOneRelationship", "parameters": [ @@ -530,7 +530,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToOneRelationship", "parameters": [ @@ -560,10 +560,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -592,7 +592,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -620,10 +620,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -652,7 +652,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -681,7 +681,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -712,7 +712,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -743,7 +743,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -773,10 +773,10 @@ } } }, - "/Resource/{id}/toOne": { + "/resources/{id}/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOne", "parameters": [ @@ -805,7 +805,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOne", "parameters": [ @@ -833,10 +833,10 @@ } } }, - "/Resource/{id}/relationships/toOne": { + "/resources/{id}/relationships/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOneRelationship", "parameters": [ @@ -865,7 +865,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOneRelationship", "parameters": [ @@ -894,7 +894,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToOneRelationship", "parameters": [ @@ -1631,7 +1631,7 @@ }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json index e861e2a99f..cbf832157e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -317,7 +317,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -348,7 +348,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -379,7 +379,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -409,10 +409,10 @@ } } }, - "/Resource/{id}/requiredToOne": { + "/resources/{id}/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOne", "parameters": [ @@ -441,7 +441,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOne", "parameters": [ @@ -469,10 +469,10 @@ } } }, - "/Resource/{id}/relationships/requiredToOne": { + "/resources/{id}/relationships/requiredToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToOneRelationship", "parameters": [ @@ -501,7 +501,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToOneRelationship", "parameters": [ @@ -530,7 +530,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToOneRelationship", "parameters": [ @@ -560,10 +560,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -592,7 +592,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -620,10 +620,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -652,7 +652,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -681,7 +681,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -712,7 +712,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -743,7 +743,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -773,10 +773,10 @@ } } }, - "/Resource/{id}/toOne": { + "/resources/{id}/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOne", "parameters": [ @@ -805,7 +805,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOne", "parameters": [ @@ -833,10 +833,10 @@ } } }, - "/Resource/{id}/relationships/toOne": { + "/resources/{id}/relationships/toOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToOneRelationship", "parameters": [ @@ -865,7 +865,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToOneRelationship", "parameters": [ @@ -894,7 +894,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToOneRelationship", "parameters": [ @@ -1667,7 +1667,7 @@ }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json index 52d81ecec1..76623a9819 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/nonNullableToOne": { + "/resources/{id}/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOne", "parameters": [ @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOne", "parameters": [ @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/nonNullableToOne": { + "/resources/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ @@ -317,7 +317,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ @@ -347,10 +347,10 @@ } } }, - "/Resource/{id}/nullableToOne": { + "/resources/{id}/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOne", "parameters": [ @@ -379,7 +379,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOne", "parameters": [ @@ -407,10 +407,10 @@ } } }, - "/Resource/{id}/relationships/nullableToOne": { + "/resources/{id}/relationships/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOneRelationship", "parameters": [ @@ -439,7 +439,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOneRelationship", "parameters": [ @@ -468,7 +468,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNullableToOneRelationship", "parameters": [ @@ -498,10 +498,10 @@ } } }, - "/Resource/{id}/requiredNonNullableToOne": { + "/resources/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ @@ -530,7 +530,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ @@ -558,10 +558,10 @@ } } }, - "/Resource/{id}/relationships/requiredNonNullableToOne": { + "/resources/{id}/relationships/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -590,7 +590,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -619,7 +619,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -649,10 +649,10 @@ } } }, - "/Resource/{id}/requiredNullableToOne": { + "/resources/{id}/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOne", "parameters": [ @@ -681,7 +681,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOne", "parameters": [ @@ -709,10 +709,10 @@ } } }, - "/Resource/{id}/relationships/requiredNullableToOne": { + "/resources/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ @@ -741,7 +741,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ @@ -770,7 +770,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ @@ -800,10 +800,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -832,7 +832,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -860,10 +860,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -892,7 +892,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -921,7 +921,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -952,7 +952,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -983,7 +983,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -1013,10 +1013,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -1045,7 +1045,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -1073,10 +1073,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -1105,7 +1105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -1134,7 +1134,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -1165,7 +1165,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -1196,7 +1196,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -2022,7 +2022,7 @@ }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json index 61ae55a60b..73f9c6048a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json @@ -5,10 +5,10 @@ "version": "1.0" }, "paths": { - "/Resource": { + "/resources": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceCollection", "responses": { @@ -26,7 +26,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceCollection", "responses": { @@ -44,7 +44,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResource", "requestBody": { @@ -73,10 +73,10 @@ } } }, - "/Resource/{id}": { + "/resources/{id}": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResource", "parameters": [ @@ -105,7 +105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResource", "parameters": [ @@ -134,7 +134,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResource", "parameters": [ @@ -175,7 +175,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResource", "parameters": [ @@ -196,10 +196,10 @@ } } }, - "/Resource/{id}/nonNullableToOne": { + "/resources/{id}/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOne", "parameters": [ @@ -228,7 +228,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOne", "parameters": [ @@ -256,10 +256,10 @@ } } }, - "/Resource/{id}/relationships/nonNullableToOne": { + "/resources/{id}/relationships/nonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNonNullableToOneRelationship", "parameters": [ @@ -288,7 +288,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNonNullableToOneRelationship", "parameters": [ @@ -317,7 +317,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNonNullableToOneRelationship", "parameters": [ @@ -347,10 +347,10 @@ } } }, - "/Resource/{id}/nullableToOne": { + "/resources/{id}/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOne", "parameters": [ @@ -379,7 +379,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOne", "parameters": [ @@ -407,10 +407,10 @@ } } }, - "/Resource/{id}/relationships/nullableToOne": { + "/resources/{id}/relationships/nullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceNullableToOneRelationship", "parameters": [ @@ -439,7 +439,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceNullableToOneRelationship", "parameters": [ @@ -468,7 +468,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceNullableToOneRelationship", "parameters": [ @@ -498,10 +498,10 @@ } } }, - "/Resource/{id}/requiredNonNullableToOne": { + "/resources/{id}/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOne", "parameters": [ @@ -530,7 +530,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOne", "parameters": [ @@ -558,10 +558,10 @@ } } }, - "/Resource/{id}/relationships/requiredNonNullableToOne": { + "/resources/{id}/relationships/requiredNonNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -590,7 +590,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -619,7 +619,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNonNullableToOneRelationship", "parameters": [ @@ -649,10 +649,10 @@ } } }, - "/Resource/{id}/requiredNullableToOne": { + "/resources/{id}/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOne", "parameters": [ @@ -681,7 +681,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOne", "parameters": [ @@ -709,10 +709,10 @@ } } }, - "/Resource/{id}/relationships/requiredNullableToOne": { + "/resources/{id}/relationships/requiredNullableToOne": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredNullableToOneRelationship", "parameters": [ @@ -741,7 +741,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredNullableToOneRelationship", "parameters": [ @@ -770,7 +770,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredNullableToOneRelationship", "parameters": [ @@ -800,10 +800,10 @@ } } }, - "/Resource/{id}/requiredToMany": { + "/resources/{id}/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToMany", "parameters": [ @@ -832,7 +832,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToMany", "parameters": [ @@ -860,10 +860,10 @@ } } }, - "/Resource/{id}/relationships/requiredToMany": { + "/resources/{id}/relationships/requiredToMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceRequiredToManyRelationship", "parameters": [ @@ -892,7 +892,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceRequiredToManyRelationship", "parameters": [ @@ -921,7 +921,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceRequiredToManyRelationship", "parameters": [ @@ -952,7 +952,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceRequiredToManyRelationship", "parameters": [ @@ -983,7 +983,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceRequiredToManyRelationship", "parameters": [ @@ -1013,10 +1013,10 @@ } } }, - "/Resource/{id}/toMany": { + "/resources/{id}/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToMany", "parameters": [ @@ -1045,7 +1045,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToMany", "parameters": [ @@ -1073,10 +1073,10 @@ } } }, - "/Resource/{id}/relationships/toMany": { + "/resources/{id}/relationships/toMany": { "get": { "tags": [ - "Resource" + "resources" ], "operationId": "getResourceToManyRelationship", "parameters": [ @@ -1105,7 +1105,7 @@ }, "head": { "tags": [ - "Resource" + "resources" ], "operationId": "headResourceToManyRelationship", "parameters": [ @@ -1134,7 +1134,7 @@ }, "post": { "tags": [ - "Resource" + "resources" ], "operationId": "postResourceToManyRelationship", "parameters": [ @@ -1165,7 +1165,7 @@ }, "patch": { "tags": [ - "Resource" + "resources" ], "operationId": "patchResourceToManyRelationship", "parameters": [ @@ -1196,7 +1196,7 @@ }, "delete": { "tags": [ - "Resource" + "resources" ], "operationId": "deleteResourceToManyRelationship", "parameters": [ @@ -2016,7 +2016,7 @@ }, "resourceResourceType": { "enum": [ - "Resource" + "resources" ], "type": "string" }, diff --git a/test/OpenApiTests/ResourceFieldValidation/Empty.cs b/test/OpenApiTests/ResourceFieldValidation/Empty.cs deleted file mode 100644 index 150c00dcb0..0000000000 --- a/test/OpenApiTests/ResourceFieldValidation/Empty.cs +++ /dev/null @@ -1,9 +0,0 @@ -using JetBrains.Annotations; -using JsonApiDotNetCore.Resources; - -namespace OpenApiTests.ResourceFieldValidation; - -[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public class Empty : Identifiable -{ -} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs index f70e8e7f7a..0b6ea5d875 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffDbContext.cs @@ -9,7 +9,8 @@ namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; [UsedImplicitly(ImplicitUseTargetFlags.Members)] public sealed class NrtOffDbContext : TestableDbContext { - public DbSet NrtOffResources => Set(); + public DbSet Resources => Set(); + public DbSet Empties => Set(); public NrtOffDbContext(DbContextOptions options) : base(options) @@ -19,16 +20,24 @@ public NrtOffDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder builder) { builder.Entity() - .HasOne(resource => resource.ToOne); + .HasOne(resource => resource.ToOne) + .WithOne() + .HasForeignKey("ToOneId"); builder.Entity() - .HasOne(resource => resource.RequiredToOne); + .HasOne(resource => resource.RequiredToOne) + .WithOne() + .HasForeignKey("RequiredToOneId"); builder.Entity() - .HasMany(resource => resource.ToMany); + .HasMany(resource => resource.ToMany) + .WithOne() + .HasForeignKey("ToManyId"); builder.Entity() - .HasMany(resource => resource.RequiredToMany); + .HasMany(resource => resource.RequiredToMany) + .WithOne() + .HasForeignKey("RequiredToManyId"); base.OnModelCreating(builder); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs new file mode 100644 index 0000000000..4df36bca01 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffEmpty.cs @@ -0,0 +1,13 @@ +#nullable disable + +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +[Resource(PublicName = "empties", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public class NrtOffEmpty : Identifiable +{ +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs index bbc028e155..968d579d49 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/NrtOffResource.cs @@ -8,7 +8,7 @@ namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOff; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +[Resource(PublicName = "resources", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] public sealed class NrtOffResource : Identifiable { [Attr] @@ -33,16 +33,16 @@ public sealed class NrtOffResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public Empty ToOne { get; set; } + public NrtOffEmpty ToOne { get; set; } [Required] [HasOne] - public Empty RequiredToOne { get; set; } + public NrtOffEmpty RequiredToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs index de405972a5..bf213ea812 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnDbContext.cs @@ -9,7 +9,8 @@ namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; [UsedImplicitly(ImplicitUseTargetFlags.Members)] public sealed class NrtOnDbContext : TestableDbContext { - public DbSet NrtOnResources => Set(); + public DbSet Resources => Set(); + public DbSet Empties => Set(); public NrtOnDbContext(DbContextOptions options) : base(options) @@ -19,22 +20,34 @@ public NrtOnDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder builder) { builder.Entity() - .HasOne(resource => resource.NonNullableToOne); + .HasOne(resource => resource.NonNullableToOne) + .WithOne() + .HasForeignKey("NonNullableToOneId"); builder.Entity() - .HasOne(resource => resource.RequiredNonNullableToOne); + .HasOne(resource => resource.RequiredNonNullableToOne) + .WithOne() + .HasForeignKey("RequiredNonNullableToOneId"); builder.Entity() - .HasOne(resource => resource.NullableToOne); + .HasOne(resource => resource.NullableToOne) + .WithOne() + .HasForeignKey("NullableToOneId"); builder.Entity() - .HasOne(resource => resource.RequiredNullableToOne); + .HasOne(resource => resource.RequiredNullableToOne) + .WithOne() + .HasForeignKey("RequiredNullableToOneId"); builder.Entity() - .HasMany(resource => resource.ToMany); + .HasMany(resource => resource.ToMany) + .WithOne() + .HasForeignKey("ToManyId"); builder.Entity() - .HasMany(resource => resource.RequiredToMany); + .HasMany(resource => resource.RequiredToMany) + .WithOne() + .HasForeignKey("RequiredToManyId"); base.OnModelCreating(builder); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs new file mode 100644 index 0000000000..65df582c71 --- /dev/null +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnEmpty.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +[Resource(PublicName = "empties", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +public class NrtOnEmpty : Identifiable +{ +} diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs index 14ad004dc9..efc897cea2 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/NrtOnResource.cs @@ -6,7 +6,7 @@ namespace OpenApiTests.ResourceFieldValidation.NullableReferenceTypesOn; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(PublicName = "Resource", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] +[Resource(PublicName = "resources", ControllerNamespace = "OpenApiTests.ResourceFieldValidation")] public sealed class NrtOnResource : Identifiable { [Attr] @@ -38,23 +38,23 @@ public sealed class NrtOnResource : Identifiable public int? RequiredNullableValueType { get; set; } [HasOne] - public Empty NonNullableToOne { get; set; } = null!; + public NrtOnEmpty NonNullableToOne { get; set; } = null!; [Required] [HasOne] - public Empty RequiredNonNullableToOne { get; set; } = null!; + public NrtOnEmpty RequiredNonNullableToOne { get; set; } = null!; [HasOne] - public Empty? NullableToOne { get; set; } + public NrtOnEmpty? NullableToOne { get; set; } [Required] [HasOne] - public Empty? RequiredNullableToOne { get; set; } + public NrtOnEmpty? RequiredNullableToOne { get; set; } [HasMany] - public ICollection ToMany { get; set; } = new HashSet(); + public ICollection ToMany { get; set; } = new HashSet(); [Required] [HasMany] - public ICollection RequiredToMany { get; set; } = new HashSet(); + public ICollection RequiredToMany { get; set; } = new HashSet(); } From 36ac8e7d063a3d307271a8bbd16e79b851e8de13 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 00:33:15 +0100 Subject: [PATCH 30/66] Move misplaced Act comments --- .../ModelStateValidationOff/CreateResourceTests.cs | 4 ++-- .../ModelStateValidationOn/CreateResourceTests.cs | 4 ++-- .../ModelStateValidationOff/CreateResourceTests.cs | 4 ++-- .../ModelStateValidationOn/CreateResourceTests.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index e88561466d..b2d3ceefc3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -219,9 +219,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } @@ -385,9 +385,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 7ad0f848d5..d5b6cc48b7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -259,9 +259,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } @@ -427,9 +427,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 1f74ce0cf7..d6abaaf2e1 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -277,9 +277,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } @@ -461,9 +461,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 5d86403aa4..0dfcad8d4e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -275,9 +275,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } @@ -461,9 +461,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - // Act using (apiClient.WithPartialAttributeSerialization(requestDocument)) { + // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); } From a88bce8474e0358621a4add94107d28bf6fd5474 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 11:58:51 +0100 Subject: [PATCH 31/66] Optimize and clarify ResourceFieldValidationMetadataProvider --- ...ResourceFieldValidationMetadataProvider.cs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs index bfbe32bb31..e40ab57618 100644 --- a/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/ResourceFieldValidationMetadataProvider.cs @@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.OpenApi; internal sealed class ResourceFieldValidationMetadataProvider { private readonly bool _validateModelState; - private readonly NullabilityInfoContext _nullabilityContext; + private readonly NullabilityInfoContext _nullabilityContext = new(); private readonly IModelMetadataProvider _modelMetadataProvider; public ResourceFieldValidationMetadataProvider(IJsonApiOptions options, IModelMetadataProvider modelMetadataProvider) @@ -20,7 +20,6 @@ public ResourceFieldValidationMetadataProvider(IJsonApiOptions options, IModelMe _validateModelState = options.ValidateModelState; _modelMetadataProvider = modelMetadataProvider; - _nullabilityContext = new NullabilityInfoContext(); } public bool IsNullable(ResourceFieldAttribute field) @@ -33,18 +32,13 @@ public bool IsNullable(ResourceFieldAttribute field) } bool hasRequiredAttribute = field.Property.HasAttribute(); - NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); - - if (field is HasManyAttribute) - { - return false; - } - if (hasRequiredAttribute && _validateModelState && nullabilityInfo.ReadState != NullabilityState.NotNull) + if (_validateModelState && hasRequiredAttribute) { return false; } + NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); return nullabilityInfo.ReadState != NullabilityState.NotNull; } @@ -64,11 +58,12 @@ public bool IsRequired(ResourceFieldAttribute field) return false; } - bool isNotNull = HasNullabilityStateNotNull(field); - bool isRequiredValueType = field.Property.PropertyType.IsValueType && hasRequiredAttribute && isNotNull; + NullabilityInfo nullabilityInfo = _nullabilityContext.Create(field.Property); + bool isRequiredValueType = field.Property.PropertyType.IsValueType && hasRequiredAttribute && nullabilityInfo.ReadState == NullabilityState.NotNull; if (isRequiredValueType) { + // Special case: ASP.NET ModelState Validation effectively ignores value types with [Required]. return false; } @@ -77,16 +72,9 @@ public bool IsRequired(ResourceFieldAttribute field) private bool IsModelStateValidationRequired(ResourceFieldAttribute field) { - ModelMetadata resourceFieldModelMetadata = _modelMetadataProvider.GetMetadataForProperties(field.Type.ClrType) - .Single(modelMetadata => modelMetadata.PropertyName! == field.Property.Name); + ModelMetadata modelMetadata = _modelMetadataProvider.GetMetadataForProperty(field.Type.ClrType, field.Property.Name); - return resourceFieldModelMetadata.ValidatorMetadata.Any(validatorMetadata => validatorMetadata is RequiredAttribute); - } - - private bool HasNullabilityStateNotNull(ResourceFieldAttribute field) - { - NullabilityInfo resourceFieldNullabilityInfo = _nullabilityContext.Create(field.Property); - bool hasNullabilityStateNotNull = resourceFieldNullabilityInfo is { ReadState: NullabilityState.NotNull, WriteState: NullabilityState.NotNull }; - return hasNullabilityStateNotNull; + // Non-nullable reference types are implicitly required, unless SuppressImplicitRequiredAttributeForNonNullableReferenceTypes is set. + return modelMetadata.ValidatorMetadata.Any(validatorMetadata => validatorMetadata is RequiredAttribute); } } From 2652063d460700e75e6353e79691cdf9af60fca6 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 12:30:50 +0100 Subject: [PATCH 32/66] Rename method, provide error message --- test/OpenApiClientTests/FakeHttpClientWrapper.cs | 4 ++-- .../ModelStateValidationOff/CreateResourceTests.cs | 14 +++++++------- .../ModelStateValidationOff/UpdateResourceTests.cs | 4 ++-- .../ModelStateValidationOn/CreateResourceTests.cs | 14 +++++++------- .../ModelStateValidationOn/UpdateResourceTests.cs | 4 ++-- .../ModelStateValidationOff/CreateResourceTests.cs | 14 +++++++------- .../ModelStateValidationOff/UpdateResourceTests.cs | 4 ++-- .../ModelStateValidationOn/CreateResourceTests.cs | 14 +++++++------- .../ModelStateValidationOn/UpdateResourceTests.cs | 4 ++-- 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/test/OpenApiClientTests/FakeHttpClientWrapper.cs b/test/OpenApiClientTests/FakeHttpClientWrapper.cs index 8226a4e496..0d3e868384 100644 --- a/test/OpenApiClientTests/FakeHttpClientWrapper.cs +++ b/test/OpenApiClientTests/FakeHttpClientWrapper.cs @@ -23,11 +23,11 @@ private FakeHttpClientWrapper(HttpClient httpClient, FakeHttpMessageHandler hand _handler = handler; } - public JsonElement ParseRequestBody() + public JsonElement GetRequestBodyAsJson() { if (RequestBody == null) { - throw new InvalidOperationException(); + throw new InvalidOperationException("No body was provided with the request."); } using JsonDocument jsonDocument = JsonDocument.Parse(RequestBody); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index b2d3ceefc3..081b4ee2e3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -52,7 +52,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -97,7 +97,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -141,7 +141,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -226,7 +226,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -266,7 +266,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -392,7 +392,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { @@ -435,7 +435,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index c5309246e8..5ae7e9b22e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -90,7 +90,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -136,7 +136,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index d5b6cc48b7..7fbab961e7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -50,7 +50,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -96,7 +96,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -183,7 +183,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -266,7 +266,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -305,7 +305,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -434,7 +434,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { @@ -478,7 +478,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index c5e0aee40d..df28db1f7c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -90,7 +90,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -136,7 +136,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index d6abaaf2e1..ce29ccd70a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -54,7 +54,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -101,7 +101,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -193,7 +193,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -284,7 +284,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -327,7 +327,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -468,7 +468,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { @@ -514,7 +514,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index cef728c02b..8c3c139ba7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -96,7 +96,7 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -146,7 +146,7 @@ public async Task Can_exclude_relationship_that_is_required_in_create_resource(s } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 0dfcad8d4e..4d75b774c5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -52,7 +52,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -100,7 +100,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -193,7 +193,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -282,7 +282,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -323,7 +323,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { @@ -468,7 +468,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { @@ -514,7 +514,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index b5a3c49e6f..972081fae7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -96,7 +96,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.attributes").With(attributesObject => { @@ -146,7 +146,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } // Assert - JsonElement document = wrapper.ParseRequestBody(); + JsonElement document = wrapper.GetRequestBodyAsJson(); document.ShouldContainPath("data.relationships").With(relationshipsObject => { From f2ecb5f1fc7d8cb8c8835af55cce2556989cb189 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 17:52:55 +0100 Subject: [PATCH 33/66] Refactor JsonApiClient: simplified recursion by using two converters, clearer naming, separation of concerns, improved error message --- .../IJsonApiClient.cs | 18 +- .../JsonApiClient.cs | 355 +++++++++--------- ...ialAttributeSerializationLifetimeTests.cs} | 18 +- .../CreateResourceTests.cs | 4 +- .../CreateResourceTests.cs | 4 +- .../CreateResourceTests.cs | 4 +- .../CreateResourceTests.cs | 4 +- 7 files changed, 208 insertions(+), 199 deletions(-) rename test/OpenApiClientTests/LegacyClient/{RequestDocumentRegistrationLifetimeTests.cs => PartialAttributeSerializationLifetimeTests.cs} (93%) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs index 5cb98a7b9a..9c71e32380 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/IJsonApiClient.cs @@ -5,22 +5,19 @@ namespace JsonApiDotNetCore.OpenApi.Client; public interface IJsonApiClient { /// + /// Ensures correct serialization of JSON:API attributes in the request body of a POST/PATCH request at a resource endpoint. Properties with default + /// values are omitted, unless explicitly included using /// - /// Calling this method ensures that attributes containing a default value (null for reference types, 0 for integers, false for - /// booleans, etc) are omitted during serialization, except for those explicitly marked for inclusion in - /// . - /// - /// - /// This is sometimes required to ensure correct serialization of attributes during a POST/PATCH request. In JSON:API, an omitted attribute indicates to - /// ignore it, while an attribute that is set to "null" means to clear it. This poses a problem because the serializer cannot distinguish between "you - /// have explicitly set this .NET property to null" vs "you didn't touch it, so it is null by default" when converting an instance to JSON. + /// In JSON:API, an omitted attribute indicates to ignore it, while an attribute that is set to null means to clear it. This poses a problem, + /// because the serializer cannot distinguish between "you have explicitly set this .NET property to its default value" vs "you didn't touch it, so it + /// contains its default value" when converting to JSON. /// /// /// /// The request document instance for which default values should be omitted. /// /// - /// Optional. A list of expressions to indicate which properties to unconditionally include in the JSON request body. For example: + /// Optional. A list of lambda expressions that indicate which properties to always include in the JSON request body. For example: /// video.Title, video => video.Summary /// ]]> @@ -33,7 +30,8 @@ public interface IJsonApiClient /// /// /// An to clear the current registration. For efficient memory usage, it is recommended to wrap calls to this method in a - /// using statement, so the registrations are cleaned up after executing the request. + /// using statement, so the registrations are cleaned up after executing the request. After disposal, the client can be reused without the + /// registrations added earlier. /// IDisposable WithPartialAttributeSerialization(TRequestDocument requestDocument, params Expression>[] alwaysIncludedAttributeSelectors) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 99bfe7d3b6..1c5bdec559 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -1,26 +1,25 @@ using System.Linq.Expressions; using System.Reflection; -using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace JsonApiDotNetCore.OpenApi.Client; /// -/// Base class to inherit auto-generated client from. Enables to mark fields to be explicitly included in a request body, even if they are null or -/// default. +/// Base class to inherit auto-generated OpenAPI clients from. Provides support for partial POST/PATCH in JSON:API requests. /// -[PublicAPI] public abstract class JsonApiClient : IJsonApiClient { - private readonly JsonApiJsonConverter _jsonApiJsonConverter = new(); + private readonly DocumentJsonConverter _documentJsonConverter = new(); + /// + /// Initial setup. Call this from the UpdateJsonSerializerSettings partial method in the auto-generated OpenAPI client. + /// protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings) { ArgumentGuard.NotNull(settings); - settings.Converters.Add(_jsonApiJsonConverter); + settings.Converters.Add(_documentJsonConverter); } /// @@ -45,9 +44,10 @@ public IDisposable WithPartialAttributeSerialization + /// Tracks a JSON:API attributes registration for a JSON:API document instance in the serializer. Disposing removes the registration, so the client can + /// be reused. + /// + private sealed class DocumentRegistrationScope : IDisposable { - private readonly Dictionary _alwaysIncludedAttributesByRequestDocument = new(); - private readonly Dictionary> _requestDocumentsByType = new(); - private SerializationScope? _serializationScope; + private readonly DocumentJsonConverter _documentJsonConverter; + private readonly object _document; + + public DocumentRegistrationScope(DocumentJsonConverter documentJsonConverter, object document) + { + ArgumentGuard.NotNull(documentJsonConverter); + ArgumentGuard.NotNull(document); + + _documentJsonConverter = documentJsonConverter; + _document = document; + } + + public void Dispose() + { + _documentJsonConverter.UnRegisterDocument(_document); + } + } + + /// + /// Represents the set of JSON:API attributes to always send to the server, even if they are uninitialized (contain default value). + /// + private sealed class AlwaysIncludedAttributes + { + private readonly ISet _propertyNames; + private readonly Type _attributesObjectType; + + public AlwaysIncludedAttributes(ISet propertyNames, Type attributesObjectType) + { + ArgumentGuard.NotNull(propertyNames); + ArgumentGuard.NotNull(attributesObjectType); + + _propertyNames = propertyNames; + _attributesObjectType = attributesObjectType; + } + + public bool ContainsAttribute(string propertyName) + { + return _propertyNames.Contains(propertyName); + } + + public bool IsAttributesObjectType(Type type) + { + return _attributesObjectType == type; + } + } + + /// + /// A that acts on JSON:API documents. + /// + private sealed class DocumentJsonConverter : JsonConverter + { + private readonly Dictionary _alwaysIncludedAttributesByDocument = new(); + private readonly Dictionary> _documentsByType = new(); + private bool _isSerializing; public override bool CanRead => false; - public void RegisterDocument(object requestDocument, AttributeNamesContainer alwaysIncludedAttributes) + public void RegisterDocument(object document, AlwaysIncludedAttributes alwaysIncludedAttributes) { - _alwaysIncludedAttributesByRequestDocument[requestDocument] = alwaysIncludedAttributes; + _alwaysIncludedAttributesByDocument[document] = alwaysIncludedAttributes; - Type requestDocumentType = requestDocument.GetType(); + Type documentType = document.GetType(); - if (!_requestDocumentsByType.ContainsKey(requestDocumentType)) + if (!_documentsByType.ContainsKey(documentType)) { - _requestDocumentsByType[requestDocumentType] = new HashSet(); + _documentsByType[documentType] = new HashSet(); } - _requestDocumentsByType[requestDocumentType].Add(requestDocument); + _documentsByType[documentType].Add(document); } - public void RemoveRegistration(object requestDocument) + public void UnRegisterDocument(object document) { - if (_alwaysIncludedAttributesByRequestDocument.ContainsKey(requestDocument)) + if (_alwaysIncludedAttributesByDocument.ContainsKey(document)) { - _alwaysIncludedAttributesByRequestDocument.Remove(requestDocument); + _alwaysIncludedAttributesByDocument.Remove(document); - Type requestDocumentType = requestDocument.GetType(); - _requestDocumentsByType[requestDocumentType].Remove(requestDocument); + Type documentType = document.GetType(); + _documentsByType[documentType].Remove(document); - if (!_requestDocumentsByType[requestDocumentType].Any()) + if (!_documentsByType[documentType].Any()) { - _requestDocumentsByType.Remove(requestDocumentType); + _documentsByType.Remove(documentType); } } } @@ -109,12 +164,13 @@ public override bool CanConvert(Type objectType) { ArgumentGuard.NotNull(objectType); - if (_serializationScope == null) + if (_isSerializing) { - return _requestDocumentsByType.ContainsKey(objectType); + // Protect against infinite recursion. + return false; } - return _serializationScope.ShouldConvertAsAttributesObject(objectType); + return _documentsByType.ContainsKey(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) @@ -125,198 +181,147 @@ public override object ReadJson(JsonReader reader, Type objectType, object? exis public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { ArgumentGuard.NotNull(writer); - ArgumentGuard.NotNull(value); ArgumentGuard.NotNull(serializer); - if (_serializationScope == null) + if (value != null) { - AssertObjectIsRequestDocument(value); - - SerializeRequestDocument(writer, value, serializer); - } - else - { - AttributeNamesContainer? attributesObjectInfo = _serializationScope.AttributesObjectInScope; - - AssertObjectMatchesSerializationScope(attributesObjectInfo, value); + if (_alwaysIncludedAttributesByDocument.TryGetValue(value, out AlwaysIncludedAttributes? alwaysIncludedAttributes)) + { + var attributesJsonConverter = new AttributesJsonConverter(alwaysIncludedAttributes); + serializer.Converters.Add(attributesJsonConverter); + } - SerializeAttributesObject(attributesObjectInfo, writer, value, serializer); + try + { + _isSerializing = true; + serializer.Serialize(writer, value); + } + finally + { + _isSerializing = false; + } } } + } - private void AssertObjectIsRequestDocument(object value) - { - Type objectType = value.GetType(); + /// + /// A that acts on JSON:API attribute objects. + /// + private sealed class AttributesJsonConverter : JsonConverter + { + private readonly AlwaysIncludedAttributes _alwaysIncludedAttributes; + private bool _isSerializing; - if (!_requestDocumentsByType.ContainsKey(objectType)) - { - throw new UnreachableCodeException(); - } - } + public override bool CanRead => false; - private void SerializeRequestDocument(JsonWriter writer, object value, JsonSerializer serializer) + public AttributesJsonConverter(AlwaysIncludedAttributes alwaysIncludedAttributes) { - _serializationScope = new SerializationScope(); - - if (_alwaysIncludedAttributesByRequestDocument.TryGetValue(value, out AttributeNamesContainer? attributesObjectInfo)) - { - _serializationScope.AttributesObjectInScope = attributesObjectInfo; - } + ArgumentGuard.NotNull(alwaysIncludedAttributes); - try - { - serializer.Serialize(writer, value); - } - finally - { - _serializationScope = null; - } + _alwaysIncludedAttributes = alwaysIncludedAttributes; } - private static void AssertObjectMatchesSerializationScope([SysNotNull] AttributeNamesContainer? attributesObjectInfo, object value) + public override bool CanConvert(Type objectType) { - Type objectType = value.GetType(); + ArgumentGuard.NotNull(objectType); - if (attributesObjectInfo == null || !attributesObjectInfo.MatchesAttributesObjectType(objectType)) + if (_isSerializing) { - throw new UnreachableCodeException(); + // Protect against infinite recursion. + return false; } + + return _alwaysIncludedAttributes.IsAttributesObjectType(objectType); } - private static void SerializeAttributesObject(AttributeNamesContainer alwaysIncludedAttributes, JsonWriter writer, object value, - JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { - AssertRequiredPropertiesAreNotExcluded(value, alwaysIncludedAttributes, writer); - - serializer.ContractResolver = new JsonApiAttributeContractResolver(alwaysIncludedAttributes); - serializer.Serialize(writer, value); + throw new UnreachableCodeException(); } - private static void AssertRequiredPropertiesAreNotExcluded(object value, AttributeNamesContainer alwaysIncludedAttributes, JsonWriter jsonWriter) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - PropertyInfo[] propertyInfos = value.GetType().GetProperties(); + ArgumentGuard.NotNull(writer); + ArgumentGuard.NotNull(serializer); - foreach (PropertyInfo attributesPropertyInfo in propertyInfos) + if (value != null) { - bool isExplicitlyIncluded = alwaysIncludedAttributes.ContainsAttribute(attributesPropertyInfo.Name); - - if (isExplicitlyIncluded) + if (_alwaysIncludedAttributes.IsAttributesObjectType(value.GetType())) { - return; + AssertRequiredAttributesHaveNonDefaultValues(value, writer.Path); + + serializer.ContractResolver = new JsonApiAttributeContractResolver(_alwaysIncludedAttributes); } - AssertRequiredPropertyIsNotIgnored(value, attributesPropertyInfo, jsonWriter.Path); + try + { + _isSerializing = true; + serializer.Serialize(writer, value); + } + finally + { + _isSerializing = false; + } } } - private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInfo attribute, string path) + private void AssertRequiredAttributesHaveNonDefaultValues(object attributesObject, string jsonPath) { - JsonPropertyAttribute jsonPropertyForAttribute = attribute.GetCustomAttributes().Single(); - - if (jsonPropertyForAttribute.Required is not (Required.Always or Required.AllowNull)) - { - return; - } - - bool isPropertyIgnored = DefaultValueEqualsCurrentValue(attribute, value); - - if (isPropertyIgnored) + foreach (PropertyInfo propertyInfo in attributesObject.GetType().GetProperties()) { - throw new InvalidOperationException($"The following property should not be omitted: {path}.{jsonPropertyForAttribute.PropertyName}."); - } - } + bool isExplicitlyIncluded = _alwaysIncludedAttributes.ContainsAttribute(propertyInfo.Name); - private static bool DefaultValueEqualsCurrentValue(PropertyInfo propertyInfo, object instance) - { - object? currentValue = propertyInfo.GetValue(instance); - object? defaultValue = GetDefaultValue(propertyInfo.PropertyType); - - if (defaultValue == null) - { - return currentValue == null; + if (!isExplicitlyIncluded) + { + AssertPropertyHasNonDefaultValueIfRequired(attributesObject, propertyInfo, jsonPath); + } } - - return defaultValue.Equals(currentValue); } - private static object? GetDefaultValue(Type type) + private static void AssertPropertyHasNonDefaultValueIfRequired(object attributesObject, PropertyInfo propertyInfo, string jsonPath) { - return type.IsValueType ? Activator.CreateInstance(type) : null; - } - } + JsonPropertyAttribute jsonProperty = propertyInfo.GetCustomAttributes().Single(); - private sealed class SerializationScope - { - private bool _isFirstAttemptToConvertAttributes = true; - public AttributeNamesContainer? AttributesObjectInScope { get; set; } - - public bool ShouldConvertAsAttributesObject(Type type) - { - if (!_isFirstAttemptToConvertAttributes || AttributesObjectInScope == null) + if (jsonProperty.Required is Required.Always or Required.AllowNull) { - return false; - } + bool propertyHasDefaultValue = PropertyHasDefaultValue(propertyInfo, attributesObject); - if (!AttributesObjectInScope.MatchesAttributesObjectType(type)) - { - return false; + if (propertyHasDefaultValue) + { + throw new InvalidOperationException( + $"Required property '{propertyInfo.Name}' at JSON path '{jsonPath}.{jsonProperty.PropertyName}' is not set. If sending its default value is intended, include it explicitly."); + } } - - _isFirstAttemptToConvertAttributes = false; - return true; - } - } - - private sealed class AttributeNamesContainer - { - private readonly ISet _attributeNames; - private readonly Type _attributesObjectType; - - public AttributeNamesContainer(ISet attributeNames, Type attributesObjectType) - { - ArgumentGuard.NotNull(attributeNames); - ArgumentGuard.NotNull(attributesObjectType); - - _attributeNames = attributeNames; - _attributesObjectType = attributesObjectType; } - public bool ContainsAttribute(string name) + private static bool PropertyHasDefaultValue(PropertyInfo propertyInfo, object instance) { - return _attributeNames.Contains(name); - } - - public bool MatchesAttributesObjectType(Type type) - { - return _attributesObjectType == type; - } - } - - private sealed class RequestDocumentRegistrationScope : IDisposable - { - private readonly JsonApiJsonConverter _jsonApiJsonConverter; - private readonly object _requestDocument; - - public RequestDocumentRegistrationScope(JsonApiJsonConverter jsonApiJsonConverter, object requestDocument) - { - ArgumentGuard.NotNull(jsonApiJsonConverter); - ArgumentGuard.NotNull(requestDocument); + object? propertyValue = propertyInfo.GetValue(instance); + object? defaultValue = GetDefaultValue(propertyInfo.PropertyType); - _jsonApiJsonConverter = jsonApiJsonConverter; - _requestDocument = requestDocument; + return EqualityComparer.Default.Equals(propertyValue, defaultValue); } - public void Dispose() + private static object? GetDefaultValue(Type type) { - _jsonApiJsonConverter.RemoveRegistration(_requestDocument); + return type.IsValueType ? Activator.CreateInstance(type) : null; } } + /// + /// Corrects the and JSON annotations at runtime, which appear on the auto-generated + /// properties for JSON:API attributes. For example: + /// + /// + /// + /// private sealed class JsonApiAttributeContractResolver : DefaultContractResolver { - private readonly AttributeNamesContainer _alwaysIncludedAttributes; + private readonly AlwaysIncludedAttributes _alwaysIncludedAttributes; - public JsonApiAttributeContractResolver(AttributeNamesContainer alwaysIncludedAttributes) + public JsonApiAttributeContractResolver(AlwaysIncludedAttributes alwaysIncludedAttributes) { ArgumentGuard.NotNull(alwaysIncludedAttributes); @@ -325,25 +330,23 @@ public JsonApiAttributeContractResolver(AttributeNamesContainer alwaysIncludedAt protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { - JsonProperty property = base.CreateProperty(member, memberSerialization); - - bool canOmitAttribute = property.Required != Required.Always; + JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization); - if (canOmitAttribute && _alwaysIncludedAttributes.MatchesAttributesObjectType(property.DeclaringType!)) + if (_alwaysIncludedAttributes.IsAttributesObjectType(jsonProperty.DeclaringType!)) { - if (_alwaysIncludedAttributes.ContainsAttribute(property.UnderlyingName!)) + if (_alwaysIncludedAttributes.ContainsAttribute(jsonProperty.UnderlyingName!)) { - property.NullValueHandling = NullValueHandling.Include; - property.DefaultValueHandling = DefaultValueHandling.Include; + jsonProperty.NullValueHandling = NullValueHandling.Include; + jsonProperty.DefaultValueHandling = DefaultValueHandling.Include; } else { - property.NullValueHandling = NullValueHandling.Ignore; - property.DefaultValueHandling = DefaultValueHandling.Ignore; + jsonProperty.NullValueHandling = NullValueHandling.Ignore; + jsonProperty.DefaultValueHandling = DefaultValueHandling.Ignore; } } - return property; + return jsonProperty; } } } diff --git a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs similarity index 93% rename from test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs rename to test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs index b6855c06d2..be71dfa1e3 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestDocumentRegistrationLifetimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs @@ -6,10 +6,10 @@ namespace OpenApiClientTests.LegacyClient; -public sealed class RequestDocumentRegistrationLifetimeTests +public sealed class PartialAttributeSerializationLifetimeTests { [Fact] - public async Task Disposed_request_document_registration_does_not_affect_request() + public async Task Disposed_registration_does_not_affect_request() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -51,7 +51,7 @@ public async Task Disposed_request_document_registration_does_not_affect_request } [Fact] - public async Task Request_document_registration_can_be_used_for_multiple_requests() + public async Task Registration_can_be_used_for_multiple_requests() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -98,7 +98,7 @@ public async Task Request_document_registration_can_be_used_for_multiple_request } [Fact] - public async Task Request_is_unaffected_by_request_document_registration_of_different_request_document_of_same_type() + public async Task Request_is_unaffected_by_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -153,7 +153,7 @@ public async Task Request_is_unaffected_by_request_document_registration_of_diff } [Fact] - public async Task Attribute_values_can_be_changed_after_request_document_registration() + public async Task Attribute_values_can_be_changed_after_registration() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -196,7 +196,7 @@ public async Task Attribute_values_can_be_changed_after_request_document_registr } [Fact] - public async Task Request_document_registration_is_unaffected_by_successive_registration_of_request_document_of_different_type() + public async Task Registration_is_unaffected_by_successive_registration_for_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -247,7 +247,7 @@ public async Task Request_document_registration_is_unaffected_by_successive_regi } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_different_request_document_of_same_type() + public async Task Registration_is_unaffected_by_preceding_disposed_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -309,7 +309,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_disposed_registration_of_request_document_of_different_type() + public async Task Registration_is_unaffected_by_preceding_disposed_registration_for_document_of_different_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); @@ -371,7 +371,7 @@ public async Task Request_document_registration_is_unaffected_by_preceding_dispo } [Fact] - public async Task Request_document_registration_is_unaffected_by_preceding_registration_of_different_request_document_of_same_type() + public async Task Registration_is_unaffected_by_preceding_registration_for_different_document_of_same_type() { // Arrange using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 081b4ee2e3..f1a0cf0503 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -187,7 +187,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 7fbab961e7..6bd62d9ab2 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -228,7 +228,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index ce29ccd70a..4208255efb 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -242,7 +242,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 4d75b774c5..1666fff24e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -242,7 +242,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"The following property should not be omitted: data.attributes.{jsonPropertyName}."); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } } From d33723089fcdb81e03724744127bf496a4aa4676 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Jan 2023 23:03:44 +0100 Subject: [PATCH 34/66] Add relationship nullability assertions in OpenAPI client tests --- .../NullabilityTests.cs | 28 +++++++++++++++-- .../NullabilityTests.cs | 28 +++++++++++++++-- .../NullabilityTests.cs | 30 +++++++++++++++++-- .../NullabilityTests.cs | 30 +++++++++++++++++-- 4 files changed, 104 insertions(+), 12 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs index 5ce66e51d2..d15ba31e2b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using FluentAssertions; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; +using TestBuildingBlocks; using Xunit; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; @@ -14,10 +15,31 @@ public sealed class NullabilityTests [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); property.Should().HaveNullabilityState(expectedState); } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.Unknown)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs index 5ef7d62faf..11e082fdb9 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using FluentAssertions; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; +using TestBuildingBlocks; using Xunit; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; @@ -14,10 +15,31 @@ public sealed class NullabilityTests [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); property.Should().HaveNullabilityState(expectedState); } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.Unknown)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.Unknown)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs index c094beef16..633133a63c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using FluentAssertions; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; +using TestBuildingBlocks; using Xunit; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; @@ -16,10 +17,33 @@ public sealed class NullabilityTests [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.Nullable)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); property.Should().HaveNullabilityState(expectedState); } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.NotNull)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs index 7774891729..83fce2a945 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using FluentAssertions; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; +using TestBuildingBlocks; using Xunit; namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; @@ -16,10 +17,33 @@ public sealed class NullabilityTests [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), NullabilityState.NotNull)] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), NullabilityState.Nullable)] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), NullabilityState.NotNull)] - public void Nullability_of_generated_property_is_as_expected(string propertyName, NullabilityState expectedState) + public void Nullability_of_generated_attribute_property_is_as_expected(string propertyName, NullabilityState expectedState) { - PropertyInfo[] properties = typeof(ResourceAttributesInPostRequest).GetProperties(); - PropertyInfo property = properties.Single(property => property.Name == propertyName); + // Act + PropertyInfo? property = typeof(ResourceAttributesInPostRequest).GetProperty(propertyName); + + // Assert + property.ShouldNotBeNull(); property.Should().HaveNullabilityState(expectedState); } + + [Theory] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), NullabilityState.Nullable)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), NullabilityState.NotNull)] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), NullabilityState.NotNull)] + public void Nullability_of_generated_relationship_property_is_as_expected(string propertyName, NullabilityState expectedState) + { + // Act + PropertyInfo? relationshipProperty = typeof(ResourceRelationshipsInPostRequest).GetProperty(propertyName); + + // Assert + relationshipProperty.ShouldNotBeNull(); + + PropertyInfo? dataProperty = relationshipProperty.PropertyType.GetProperty("Data"); + dataProperty.ShouldNotBeNull(); + dataProperty.Should().HaveNullabilityState(expectedState); + } } From 52cc1f1eba48be1147ef285f2206689c4613de54 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 00:01:05 +0100 Subject: [PATCH 35/66] Cleanup JsonElementExtensions --- .../CreateResourceTests.cs | 24 +- .../UpdateResourceTests.cs | 8 +- .../CreateResourceTests.cs | 24 +- .../UpdateResourceTests.cs | 8 +- .../CreateResourceTests.cs | 24 +- .../UpdateResourceTests.cs | 8 +- .../CreateResourceTests.cs | 24 +- .../UpdateResourceTests.cs | 8 +- .../CamelCase/CamelCaseTests.cs | 240 +++++++++--------- .../KebabCase/KebabCaseTests.cs | 240 +++++++++--------- .../PascalCase/PascalCaseTests.cs | 240 +++++++++--------- .../NullabilityTests.cs | 24 +- .../ModelStateValidationOff/RequiredTests.cs | 28 +- .../NullabilityTests.cs | 24 +- .../ModelStateValidationOn/RequiredTests.cs | 28 +- .../NullabilityTests.cs | 24 +- .../ModelStateValidationOff/RequiredTests.cs | 28 +- .../NullabilityTests.cs | 24 +- .../ModelStateValidationOn/RequiredTests.cs | 28 +- ...s.cs => JsonElementAssertionExtensions.cs} | 96 +++---- 20 files changed, 578 insertions(+), 574 deletions(-) rename test/TestBuildingBlocks/{JsonElementExtensions.cs => JsonElementAssertionExtensions.cs} (56%) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index f1a0cf0503..b082001055 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -54,9 +54,9 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -99,9 +99,9 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -143,9 +143,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -230,7 +230,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -270,7 +270,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -396,9 +396,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -439,9 +439,9 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 5ae7e9b22e..949471c6c9 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -92,9 +92,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -138,9 +138,9 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 6bd62d9ab2..5aa18d0f35 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -52,9 +52,9 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -98,9 +98,9 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -185,9 +185,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -270,7 +270,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -309,7 +309,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -438,9 +438,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -482,9 +482,9 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index df28db1f7c..b3bca868ad 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -92,9 +92,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -138,9 +138,9 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 4208255efb..7c5179ad92 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -56,9 +56,9 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -103,9 +103,9 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -195,9 +195,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -288,7 +288,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -331,7 +331,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -472,9 +472,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -518,9 +518,9 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 8c3c139ba7..1d2f17760e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -98,9 +98,9 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -148,9 +148,9 @@ public async Task Can_exclude_relationship_that_is_required_in_create_resource(s // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 1666fff24e..9360b1bb9b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -54,9 +54,9 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); }); } @@ -102,9 +102,9 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldContainPath(jsonPropertyName).With(attribute => attribute.ShouldBeInteger(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); }); } @@ -195,9 +195,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -286,7 +286,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -327,7 +327,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => + document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => { relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); }); @@ -472,9 +472,9 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } @@ -518,9 +518,9 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 972081fae7..29e87db27b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -98,9 +98,9 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.attributes").With(attributesObject => + document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.ShouldNotContainPath(jsonPropertyName); + attributesObject.Should().NotContainPath(jsonPropertyName); }); } @@ -148,9 +148,9 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); - document.ShouldContainPath("data.relationships").With(relationshipsObject => + document.Should().ContainPath("data.relationships").With(relationshipsObject => { - relationshipsObject.ShouldNotContainPath(jsonPropertyName); + relationshipsObject.Should().NotContainPath(jsonPropertyName); }); } } diff --git a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs index e0bbd5bad3..9a694feab5 100644 --- a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs @@ -25,34 +25,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.get").With(getElement => + document.Should().ContainPath("paths./supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCollection"); + operationElement.Should().Be("getSupermarketCollection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapiObject"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapiObject"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceCollectionDocument").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarketDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarketDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -67,68 +67,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceObject") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceObject") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarketResourceType") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarketResourceType") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("supermarketAttributesInResponse").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarketRelationshipsInResponse").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("supermarkets"); + enumValueElement.Should().Be("supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("nameOfCity"); propertiesElement.Should().ContainProperty("kind"); - propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarketType"); + propertiesElement.Should().ContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarketType"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("storeManager"); - propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInResponse"); + propertiesElement.Should().ContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInResponse"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backupStoreManager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("backupStoreManager.$ref") .ShouldBeSchemaReferenceId("nullableToOneStaffMemberInResponse").SchemaReferenceId; propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInResponse"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInResponse"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInRelationshipObject") - .SchemaReferenceId; + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("linksInRelationshipObject").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -136,13 +136,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staffMemberResourceType") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("staffMemberResourceType") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staffMembers"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staffMembers"); }); } @@ -155,28 +155,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarket"); + operationElement.Should().Be("getSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPrimaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceDocument") + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceDocument") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -193,30 +193,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/storeManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/storeManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketStoreManager"); + operationElement.Should().Be("getSupermarketStoreManager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberSecondaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staffMemberDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("staffMemberDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staffMemberAttributesInResponse"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staffMemberAttributesInResponse"); }); }); } @@ -228,14 +228,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/backupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/backupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketBackupStoreManager"); + operationElement.Should().Be("getSupermarketBackupStoreManager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullableStaffMemberSecondaryResponseDocument"); }); } @@ -247,14 +247,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCashiers"); + operationElement.Should().Be("getSupermarketCashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberCollectionResponseDocument"); }); } @@ -268,28 +268,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/storeManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketStoreManagerRelationship"); + operationElement.Should().Be("getSupermarketStoreManagerRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifierResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceIdentifierDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -305,14 +305,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("getSupermarketBackupStoreManagerRelationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullableStaffMemberIdentifierResponseDocument"); }); } @@ -326,28 +326,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("getSupermarketCashiersRelationship"); + operationElement.Should().Be("getSupermarketCashiersRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("linksInResourceIdentifierCollectionDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -369,47 +369,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.post").With(getElement => + document.Should().ContainPath("paths./supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("postSupermarket"); + operationElement.Should().Be("postSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPostRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPostRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPostRequest") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPostRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPostRequest"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarketRelationshipsInPostRequest").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("storeManager"); - propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("backupStoreManager"); - propertiesElement.ShouldContainPath("backupStoreManager.$ref").ShouldBeSchemaReferenceId("nullableToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("backupStoreManager.$ref").ShouldBeSchemaReferenceId("nullableToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInRequest"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInRequest"); }); }); } @@ -421,11 +421,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("postSupermarketCashiersRelationship"); + operationElement.Should().Be("postSupermarketCashiersRelationship"); }); }); } @@ -439,31 +439,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarket"); + operationElement.Should().Be("patchSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarketPatchRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPatchRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPatchRequest") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPatchRequest"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarketRelationshipsInPatchRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPatchRequest"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarketRelationshipsInPatchRequest"); }); }); } @@ -475,11 +475,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/storeManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketStoreManagerRelationship"); + operationElement.Should().Be("patchSupermarketStoreManagerRelationship"); }); }); } @@ -491,11 +491,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("patchSupermarketBackupStoreManagerRelationship"); }); }); } @@ -507,11 +507,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patchSupermarketCashiersRelationship"); + operationElement.Should().Be("patchSupermarketCashiersRelationship"); }); }); } @@ -523,11 +523,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("deleteSupermarket"); + operationElement.Should().Be("deleteSupermarket"); }); }); } @@ -539,11 +539,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("deleteSupermarketCashiersRelationship"); + operationElement.Should().Be("deleteSupermarketCashiersRelationship"); }); }); } diff --git a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs index 0a532a6e3f..e0447fc482 100644 --- a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs @@ -25,34 +25,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.get").With(getElement => + document.Should().ContainPath("paths./supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-collection"); + operationElement.Should().Be("get-supermarket-collection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-collection-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapi-object"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapi-object"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-collection-document").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-response") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-response") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -67,68 +67,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-object") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-object") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarket-resource-type") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarket-resource-type") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("supermarket-attributes-in-response").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarket-relationships-in-response").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("supermarkets"); + enumValueElement.Should().Be("supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("name-of-city"); propertiesElement.Should().ContainProperty("kind"); - propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarket-type"); + propertiesElement.Should().ContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarket-type"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("store-manager"); - propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-response"); + propertiesElement.Should().ContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-response"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backup-store-manager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("backup-store-manager.$ref") .ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-response").SchemaReferenceId; propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-response"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-response"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-relationship-object").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("staff-member-identifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("null-value"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("null-value"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -136,13 +136,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staff-member-resource-type") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("staff-member-resource-type") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staff-members"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staff-members"); }); } @@ -155,28 +155,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket"); + operationElement.Should().Be("get-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-primary-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-document") - .SchemaReferenceId; + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-resource-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -193,30 +193,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-store-manager"); + operationElement.Should().Be("get-supermarket-store-manager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-secondary-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staff-member-data-in-response") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("staff-member-data-in-response") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staff-member-attributes-in-response"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staff-member-attributes-in-response"); }); }); } @@ -228,14 +228,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/backup-store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/backup-store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-backup-store-manager"); + operationElement.Should().Be("get-supermarket-backup-store-manager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullable-staff-member-secondary-response-document"); }); } @@ -247,14 +247,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-cashiers"); + operationElement.Should().Be("get-supermarket-cashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-collection-response-document"); }); } @@ -268,28 +268,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-store-manager-relationship"); + operationElement.Should().Be("get-supermarket-store-manager-relationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-identifier-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-identifier-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -305,14 +305,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-backup-store-manager-relationship"); + operationElement.Should().Be("get-supermarket-backup-store-manager-relationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("nullable-staff-member-identifier-response-document"); }); } @@ -326,28 +326,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("get-supermarket-cashiers-relationship"); + operationElement.Should().Be("get-supermarket-cashiers-relationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("staff-member-identifier-collection-response-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("links-in-resource-identifier-collection-document").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -369,47 +369,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets.post").With(getElement => + document.Should().ContainPath("paths./supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("post-supermarket"); + operationElement.Should().Be("post-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-post-request-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-post-request") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-post-request") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-post-request"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-post-request"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("supermarket-relationships-in-post-request").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("store-manager"); - propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-request"); + propertiesElement.Should().ContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-request"); propertiesElement.Should().ContainProperty("backup-store-manager"); - propertiesElement.ShouldContainPath("backup-store-manager.$ref").ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-request"); + propertiesElement.Should().ContainPath("backup-store-manager.$ref").ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-request"); propertiesElement.Should().ContainProperty("cashiers"); - propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-request"); + propertiesElement.Should().ContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-request"); }); }); } @@ -421,11 +421,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("post-supermarket-cashiers-relationship"); + operationElement.Should().Be("post-supermarket-cashiers-relationship"); }); }); } @@ -439,31 +439,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket"); + operationElement.Should().Be("patch-supermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("supermarket-patch-request-document").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-patch-request") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-patch-request") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-patch-request"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarket-relationships-in-patch-request"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-patch-request"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarket-relationships-in-patch-request"); }); }); } @@ -475,11 +475,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/store-manager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-store-manager-relationship"); + operationElement.Should().Be("patch-supermarket-store-manager-relationship"); }); }); } @@ -491,11 +491,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-backup-store-manager-relationship"); + operationElement.Should().Be("patch-supermarket-backup-store-manager-relationship"); }); }); } @@ -507,11 +507,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("patch-supermarket-cashiers-relationship"); + operationElement.Should().Be("patch-supermarket-cashiers-relationship"); }); }); } @@ -523,11 +523,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("delete-supermarket"); + operationElement.Should().Be("delete-supermarket"); }); }); } @@ -539,11 +539,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + document.Should().ContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("delete-supermarket-cashiers-relationship"); + operationElement.Should().Be("delete-supermarket-cashiers-relationship"); }); }); } diff --git a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs index 73868dcdad..48497186c5 100644 --- a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs +++ b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs @@ -26,34 +26,34 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCollection"); + operationElement.Should().Be("GetSupermarketCollection"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceCollectionDocumentSchemaRefId = null; string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("JsonapiObject"); + propertiesElement.Should().ContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("JsonapiObject"); - linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceCollectionDocument").SchemaReferenceId; - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("SupermarketDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.items.$ref").ShouldBeSchemaReferenceId("SupermarketDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -68,68 +68,68 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? resourceAttributesInResponseSchemaRefId = null; string? resourceRelationshipInResponseSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceObject") + linksInResourceObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceObject") .SchemaReferenceId; - primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("SupermarketResourceType") + primaryResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("SupermarketResourceType") .SchemaReferenceId; - resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + resourceAttributesInResponseSchemaRefId = propertiesElement.Should().ContainPath("attributes.$ref") .ShouldBeSchemaReferenceId("SupermarketAttributesInResponse").SchemaReferenceId; - resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInResponseSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("SupermarketRelationshipsInResponse").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); }); - schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + schemasElement.Should().ContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => { - enumValueElement.ShouldBeString("Supermarkets"); + enumValueElement.Should().Be("Supermarkets"); }); - schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("NameOfCity"); propertiesElement.Should().ContainProperty("Kind"); - propertiesElement.ShouldContainPath("Kind.$ref").ShouldBeSchemaReferenceId("SupermarketType"); + propertiesElement.Should().ContainPath("Kind.$ref").ShouldBeSchemaReferenceId("SupermarketType"); }); string? nullableToOneResourceResponseDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("StoreManager"); - propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInResponse"); + propertiesElement.Should().ContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInResponse"); - nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("BackupStoreManager.$ref") + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.Should().ContainPath("BackupStoreManager.$ref") .ShouldBeSchemaReferenceId("NullableToOneStaffMemberInResponse").SchemaReferenceId; propertiesElement.Should().ContainProperty("Cashiers"); - propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInResponse"); + propertiesElement.Should().ContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInResponse"); }); string? linksInRelationshipObjectSchemaRefId = null; string? relatedResourceIdentifierSchemaRefId = null; - schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => { - linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInRelationshipObject") - .SchemaReferenceId; + linksInRelationshipObjectSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") + .ShouldBeSchemaReferenceId("LinksInRelationshipObject").SchemaReferenceId; - relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + relatedResourceIdentifierSchemaRefId = propertiesElement.Should().ContainPath("data.oneOf[0].$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifier").SchemaReferenceId; - propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("NullValue"); + propertiesElement.Should().ContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("NullValue"); }); - schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("related"); @@ -137,13 +137,13 @@ public async Task Casing_convention_is_applied_to_GetCollection_endpoint() string? relatedResourceTypeSchemaRefId = null; - schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => { - relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("StaffMemberResourceType") + relatedResourceTypeSchemaRefId = propertiesElement.Should().ContainPath("type.$ref").ShouldBeSchemaReferenceId("StaffMemberResourceType") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("StaffMembers"); + schemasElement.Should().ContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("StaffMembers"); }); } @@ -156,28 +156,28 @@ public async Task Casing_convention_is_applied_to_GetSingle_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarket"); + operationElement.Should().Be("GetSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPrimaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceDocument") + linksInResourceDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceDocument") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -194,30 +194,30 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_sin // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/StoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/StoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketStoreManager"); + operationElement.Should().Be("GetSupermarketStoreManager"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberSecondaryResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("StaffMemberDataInResponse") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("StaffMemberDataInResponse") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("StaffMemberAttributesInResponse"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("StaffMemberAttributesInResponse"); }); }); } @@ -229,14 +229,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nul JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/BackupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/BackupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketBackupStoreManager"); + operationElement.Should().Be("GetSupermarketBackupStoreManager"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("NullableStaffMemberSecondaryResponseDocument"); }); } @@ -248,14 +248,14 @@ public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/Cashiers.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/Cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCashiers"); + operationElement.Should().Be("GetSupermarketCashiers"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberCollectionResponseDocument"); }); } @@ -269,28 +269,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/StoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketStoreManagerRelationship"); + operationElement.Should().Be("GetSupermarketStoreManagerRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifierResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceIdentifierDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -306,14 +306,14 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("GetSupermarketBackupStoreManagerRelationship"); }); - getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("NullableStaffMemberIdentifierResponseDocument"); }); } @@ -327,28 +327,28 @@ public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.get").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.get").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("GetSupermarketCashiersRelationship"); + operationElement.Should().Be("GetSupermarketCashiersRelationship"); }); - documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("StaffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.Should().ContainPath("links.$ref") .ShouldBeSchemaReferenceId("LinksInResourceIdentifierCollectionDocument").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("self"); propertiesElement.Should().ContainProperty("describedby"); @@ -370,47 +370,47 @@ public async Task Casing_convention_is_applied_to_Post_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets.post").With(getElement => + document.Should().ContainPath("paths./Supermarkets.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PostSupermarket"); + operationElement.Should().Be("PostSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPostRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPostRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPostRequest") .SchemaReferenceId; }); string? resourceRelationshipInPostRequestSchemaRefId = null; - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPostRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPostRequest"); - resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.Should().ContainPath("relationships.$ref") .ShouldBeSchemaReferenceId("SupermarketRelationshipsInPostRequest").SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => { propertiesElement.Should().ContainProperty("StoreManager"); - propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("BackupStoreManager"); - propertiesElement.ShouldContainPath("BackupStoreManager.$ref").ShouldBeSchemaReferenceId("NullableToOneStaffMemberInRequest"); + propertiesElement.Should().ContainPath("BackupStoreManager.$ref").ShouldBeSchemaReferenceId("NullableToOneStaffMemberInRequest"); propertiesElement.Should().ContainProperty("Cashiers"); - propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInRequest"); + propertiesElement.Should().ContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInRequest"); }); }); } @@ -422,11 +422,11 @@ public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.post").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.post").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PostSupermarketCashiersRelationship"); + operationElement.Should().Be("PostSupermarketCashiersRelationship"); }); }); } @@ -440,31 +440,31 @@ public async Task Casing_convention_is_applied_to_Patch_endpoint() // Assert string? documentSchemaRefId = null; - document.ShouldContainPath("paths./Supermarkets/{id}.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarket"); + operationElement.Should().Be("PatchSupermarket"); }); - documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + documentSchemaRefId = getElement.Should().ContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") .ShouldBeSchemaReferenceId("SupermarketPatchRequestDocument").SchemaReferenceId; }); - document.ShouldContainPath("components.schemas").With(schemasElement => + document.Should().ContainPath("components.schemas").With(schemasElement => { string? resourceDataSchemaRefId = null; - schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => { - resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPatchRequest") + resourceDataSchemaRefId = propertiesElement.Should().ContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPatchRequest") .SchemaReferenceId; }); - schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + schemasElement.Should().ContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => { - propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPatchRequest"); - propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("SupermarketRelationshipsInPatchRequest"); + propertiesElement.Should().ContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPatchRequest"); + propertiesElement.Should().ContainPath("relationships.$ref").ShouldBeSchemaReferenceId("SupermarketRelationshipsInPatchRequest"); }); }); } @@ -476,11 +476,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/StoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketStoreManagerRelationship"); + operationElement.Should().Be("PatchSupermarketStoreManagerRelationship"); }); }); } @@ -492,11 +492,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketBackupStoreManagerRelationship"); + operationElement.Should().Be("PatchSupermarketBackupStoreManagerRelationship"); }); }); } @@ -508,11 +508,11 @@ public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_wit JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.patch").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.patch").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("PatchSupermarketCashiersRelationship"); + operationElement.Should().Be("PatchSupermarketCashiersRelationship"); }); }); } @@ -524,11 +524,11 @@ public async Task Casing_convention_is_applied_to_Delete_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}.delete").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("DeleteSupermarket"); + operationElement.Should().Be("DeleteSupermarket"); }); }); } @@ -540,11 +540,11 @@ public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.delete").With(getElement => + document.Should().ContainPath("paths./Supermarkets/{id}/relationships/Cashiers.delete").With(getElement => { - getElement.ShouldContainPath("operationId").With(operationElement => + getElement.Should().ContainPath("operationId").With(operationElement => { - operationElement.ShouldBeString("DeleteSupermarketCashiersRelationship"); + operationElement.Should().Be("DeleteSupermarketCashiersRelationship"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs index 1fc5394d5c..d02338445e 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/NullabilityTests.cs @@ -28,11 +28,11 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } @@ -46,11 +46,11 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.Should().NotContainPath("nullable"); }); }); } @@ -64,11 +64,11 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } @@ -82,11 +82,11 @@ public async Task Schema_property_for_relationship_is_not_nullable(string jsonPr JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs index 78d67b97c6..e4bd230596 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs @@ -25,10 +25,10 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -42,10 +42,10 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -58,10 +58,10 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -74,10 +74,10 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -88,7 +88,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); } [Fact] @@ -98,6 +98,6 @@ public async Task No_relationship_schema_properties_are_required_when_updating_r JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); } } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs index 410c9d2098..185cd9a5e1 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/NullabilityTests.cs @@ -26,11 +26,11 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } @@ -46,11 +46,11 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.Should().NotContainPath("nullable"); }); }); } @@ -63,11 +63,11 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } @@ -82,11 +82,11 @@ public async Task Schema_property_for_relationship_is_not_nullable(string jsonPr JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs index dc343a3da0..f07cb7b2cb 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs @@ -24,10 +24,10 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -42,10 +42,10 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -57,10 +57,10 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -74,10 +74,10 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -88,7 +88,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); } [Fact] @@ -98,6 +98,6 @@ public async Task No_relationship_schema_properties_are_required_when_updating_r JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); } } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs index c51bef61ce..a41c3f7508 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/NullabilityTests.cs @@ -28,11 +28,11 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } @@ -48,11 +48,11 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.Should().NotContainPath("nullable"); }); }); } @@ -66,11 +66,11 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } @@ -86,11 +86,11 @@ public async Task Schema_property_for_relationship_is_not_nullable(string jsonPr JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs index 34f7da02fe..7dfd5e7ef2 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs @@ -26,10 +26,10 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -44,10 +44,10 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -61,10 +61,10 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -78,10 +78,10 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -92,7 +92,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); } [Fact] @@ -102,6 +102,6 @@ public async Task No_relationship_schema_properties_are_required_when_updating_r JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); } } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs index fb3c71a915..2d1cf8c117 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/NullabilityTests.cs @@ -26,11 +26,11 @@ public async Task Schema_property_for_attribute_is_nullable(string jsonPropertyN JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); + schemaProperty.Should().ContainPath("nullable").With(nullableProperty => nullableProperty.ValueKind.Should().Be(JsonValueKind.True)); }); }); } @@ -48,11 +48,11 @@ public async Task Schema_property_for_attribute_is_not_nullable(string jsonPrope JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceAttributesInResponse.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath(jsonPropertyName).With(schemaProperty => + schemaProperties.Should().ContainPath(jsonPropertyName).With(schemaProperty => { - schemaProperty.ShouldNotContainPath("nullable"); + schemaProperty.Should().NotContainPath("nullable"); }); }); } @@ -65,11 +65,11 @@ public async Task Schema_property_for_relationship_is_nullable(string jsonProper JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); }); }); } @@ -86,11 +86,11 @@ public async Task Schema_property_for_relationship_is_not_nullable(string jsonPr JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest.properties").With(schemaProperties => { - schemaProperties.ShouldContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => + schemaProperties.Should().ContainPath($"{jsonPropertyName}.$ref").WithSchemaReferenceId(schemaReferenceId => { - document.ShouldContainPath($"components.schemas.{schemaReferenceId}.properties.data").ShouldNotContainPath("oneOf[1].$ref"); + document.Should().ContainPath($"components.schemas.{schemaReferenceId}.properties.data").Should().NotContainPath("oneOf[1].$ref"); }); }); } diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs index af21ca967f..6d7c4a5420 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs @@ -26,10 +26,10 @@ public async Task Schema_property_for_attribute_is_required_for_creating_resourc JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -44,10 +44,10 @@ public async Task Schema_property_for_attribute_is_not_required_for_creating_res JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => + document.Should().ContainPath("components.schemas.resourceAttributesInPostRequest").With(attributesObjectSchema => { - attributesObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - attributesObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + attributesObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + attributesObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -61,10 +61,10 @@ public async Task Schema_property_for_relationship_is_required_for_creating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().ContainArrayElement(jsonPropertyName)); }); } @@ -78,10 +78,10 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => + document.Should().ContainPath("components.schemas.resourceRelationshipsInPostRequest").With(relationshipsObjectSchema => { - relationshipsObjectSchema.ShouldContainPath($"properties.{jsonPropertyName}"); - relationshipsObjectSchema.ShouldContainPath("required").With(propertySet => propertySet.ShouldBeArrayWithoutElement(jsonPropertyName)); + relationshipsObjectSchema.Should().ContainPath($"properties.{jsonPropertyName}"); + relationshipsObjectSchema.Should().ContainPath("required").With(propertySet => propertySet.Should().NotContainArrayElement(jsonPropertyName)); }); } @@ -92,7 +92,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceAttributesInPatchRequest.required"); } [Fact] @@ -102,6 +102,6 @@ public async Task No_relationship_schema_properties_are_required_when_updating_r JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.ShouldNotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); + document.Should().NotContainPath("components.schemas.resourceRelationshipsInPatchRequest.required"); } } diff --git a/test/TestBuildingBlocks/JsonElementExtensions.cs b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs similarity index 56% rename from test/TestBuildingBlocks/JsonElementExtensions.cs rename to test/TestBuildingBlocks/JsonElementAssertionExtensions.cs index 6eafecc4ca..e73a217d81 100644 --- a/test/TestBuildingBlocks/JsonElementExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using BlushingPenguin.JsonPath; using FluentAssertions; using FluentAssertions.Execution; @@ -6,54 +6,14 @@ namespace TestBuildingBlocks; -public static class JsonElementExtensions +public static class JsonElementAssertionExtensions { public static JsonElementAssertions Should(this JsonElement source) { return new JsonElementAssertions(source); } - public static JsonElement ShouldContainPath(this JsonElement source, string path) - { - Func elementSelector = () => source.SelectToken(path, true)!.Value; - return elementSelector.Should().NotThrow().Subject; - } - - public static void ShouldNotContainPath(this JsonElement source, string path) - { - JsonElement? pathToken = source.SelectToken(path); - - pathToken.Should().BeNull(); - } - - public static void ShouldBeString(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.String); - source.GetString().Should().Be(value); - } - - public static void ShouldBeArrayWithElement(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.Array); - - var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); - deserializedCollection.Should().Contain(value); - } - - public static void ShouldBeArrayWithoutElement(this JsonElement source, string value) - { - source.ValueKind.Should().Be(JsonValueKind.Array); - - var deserializedCollection = JsonSerializer.Deserialize>(source.GetRawText()); - deserializedCollection.Should().NotContain(value); - } - - public static void ShouldBeInteger(this JsonElement source, int value) - { - source.ValueKind.Should().Be(JsonValueKind.Number); - source.GetInt32().Should().Be(value); - } - + [CustomAssertion] public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElement source, string value) { string schemaReferenceId = GetSchemaReferenceId(source); @@ -62,6 +22,7 @@ public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElem return new SchemaReferenceIdContainer(value); } + [CustomAssertion] private static string GetSchemaReferenceId(this JsonElement source) { source.ValueKind.Should().Be(JsonValueKind.String); @@ -69,10 +30,10 @@ private static string GetSchemaReferenceId(this JsonElement source) string? jsonElementValue = source.GetString(); jsonElementValue.ShouldNotBeNull(); - string schemaReferenceId = jsonElementValue.Split('/').Last(); - return schemaReferenceId; + return jsonElementValue.Split('/').Last(); } + [CustomAssertion] public static void WithSchemaReferenceId(this JsonElement subject, [InstantHandle] Action continuation) { string schemaReferenceId = GetSchemaReferenceId(subject); @@ -111,11 +72,54 @@ protected JsonElementAssertions(JsonElement subject) public void ContainProperty(string propertyName) { string json = _subject.ToString(); - string escapedJson = json.Replace("{", "{{").Replace("}", "}}"); Execute.Assertion.ForCondition(_subject.TryGetProperty(propertyName, out _)) .FailWith($"Expected JSON element '{escapedJson}' to contain a property named '{propertyName}'."); } + + public JsonElement ContainPath(string path) + { + Func elementSelector = () => _subject.SelectToken(path, true)!.Value; + return elementSelector.Should().NotThrow().Subject; + } + + public void NotContainPath(string path) + { + JsonElement? pathToken = _subject.SelectToken(path); + pathToken.Should().BeNull(); + } + + public void Be(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.String); + _subject.GetString().Should().Be(value); + } + + public void Be(int value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Number); + _subject.GetInt32().Should().Be(value); + } + + public void ContainArrayElement(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Array); + + string?[] stringValues = _subject.EnumerateArray().Where(element => element.ValueKind == JsonValueKind.String) + .Select(element => element.GetString()).ToArray(); + + stringValues.Should().Contain(value); + } + + public void NotContainArrayElement(string value) + { + _subject.ValueKind.Should().Be(JsonValueKind.Array); + + string?[] stringValues = _subject.EnumerateArray().Where(element => element.ValueKind == JsonValueKind.String) + .Select(element => element.GetString()).ToArray(); + + stringValues.Should().NotContain(value); + } } } From b30a680a951a6bbec45c10c5c3871290d994ba7a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 00:19:10 +0100 Subject: [PATCH 36/66] Cleanup ObjectExtensions --- test/OpenApiClientTests/ObjectExtensions.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs index 9bf15b6ac7..c98e0fe5de 100644 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -10,8 +10,7 @@ internal static class ObjectExtensions ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - + PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); return propertyInfo.GetValue(source); } @@ -20,8 +19,7 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); - + PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); propertyInfo.SetValue(source, value); } @@ -30,8 +28,19 @@ public static void SetPropertyValue(this object source, string propertyName, obj ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(propertyName); - PropertyInfo propertyInfo = source.GetType().GetProperties().Single(property => property.Name == propertyName); + PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); + return propertyInfo.PropertyType.IsValueType ? Activator.CreateInstance(propertyInfo.PropertyType) : null; + } + + private static PropertyInfo GetExistingProperty(Type type, string propertyName) + { + PropertyInfo? propertyInfo = type.GetProperty(propertyName); + + if (propertyInfo == null) + { + throw new InvalidOperationException($"Type '{type}' does not contain a property named '{propertyName}'."); + } - return Activator.CreateInstance(propertyInfo.PropertyType); + return propertyInfo; } } From 73c3232f4fa25a5ba38abe41d4eb1ebdb51e7d38 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 00:55:36 +0100 Subject: [PATCH 37/66] Make base type abstract, remove redundant TranslateAsync calls, inline relationship Data property name --- test/OpenApiClientTests/OpenApiClientTests.cs | 4 +- .../CreateResourceTests.cs | 50 ++++++------ .../UpdateResourceTests.cs | 2 +- .../CreateResourceTests.cs | 65 +++++++-------- .../UpdateResourceTests.cs | 2 +- .../CreateResourceTests.cs | 79 ++++++++----------- .../UpdateResourceTests.cs | 2 +- .../CreateResourceTests.cs | 79 ++++++++----------- .../UpdateResourceTests.cs | 2 +- 9 files changed, 120 insertions(+), 165 deletions(-) diff --git a/test/OpenApiClientTests/OpenApiClientTests.cs b/test/OpenApiClientTests/OpenApiClientTests.cs index bb0b5d61a1..8b1987f5b1 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.cs +++ b/test/OpenApiClientTests/OpenApiClientTests.cs @@ -2,11 +2,11 @@ namespace OpenApiClientTests; -public class OpenApiClientTests +public abstract class OpenApiClientTests { private const string AttributesObjectParameterName = "attributesObject"; - protected static Expression> CreateIncludedAttributeSelector(string propertyName) + protected static Expression> CreateAttributeSelectorFor(string propertyName) where TAttributesObject : class { Type attributesObjectType = typeof(TAttributesObject); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index b082001055..bb39bf475a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -12,6 +12,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.M public sealed class CreateResourceTests : OpenApiClientTests { + private const string DataPropertyName = "Data"; private readonly NrtOffMsvOffFakers _fakers = new(); [Theory] @@ -43,7 +44,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { @@ -85,7 +86,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -181,8 +182,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -194,10 +194,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -216,7 +215,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -237,10 +236,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -259,7 +257,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -277,10 +275,9 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -299,7 +296,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -307,8 +304,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -320,10 +316,9 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -342,14 +337,13 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 949471c6c9..69b4a37ebf 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -40,7 +40,7 @@ public async Task Cannot_exclude_id() var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 5aa18d0f35..6b2fae129b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -12,6 +12,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.M public sealed class CreateResourceTests : OpenApiClientTests { + private const string DataPropertyName = "Data"; private readonly NrtOffMsvOnFakers _fakers = new(); [Theory] @@ -41,7 +42,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { @@ -84,7 +85,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -106,7 +107,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -124,19 +125,18 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -222,8 +222,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -235,9 +234,8 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -256,7 +254,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -277,9 +275,8 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), nameof(ResourceRelationshipsInPostRequest.ToOne.Data), "toOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -298,7 +295,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -316,11 +313,10 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -339,7 +335,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -347,8 +343,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -360,11 +355,10 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), nameof(ResourceRelationshipsInPostRequest.RequiredToOne.Data), "requiredToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -383,14 +377,13 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -518,8 +511,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -558,8 +550,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index b3bca868ad..6f02e27b29 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -40,7 +40,7 @@ public async Task Cannot_exclude_id() var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 7c5179ad92..abb201b582 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -12,6 +12,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.Mo public sealed class CreateResourceTests : OpenApiClientTests { + private const string DataPropertyName = "Data"; private readonly NrtOnMsvOffFakers _fakers = new(); [Theory] @@ -45,7 +46,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { @@ -89,7 +90,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -112,7 +113,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -132,19 +133,18 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -236,8 +236,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -249,11 +248,9 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -274,7 +271,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -295,11 +292,9 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -320,7 +315,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -338,14 +333,11 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -366,7 +358,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -374,8 +366,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -387,14 +378,11 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -415,14 +403,13 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -556,8 +543,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -598,8 +584,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 1d2f17760e..dedaf8f5a9 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -42,7 +42,7 @@ public async Task Cannot_exclude_id() var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 9360b1bb9b..c2c1993d47 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -12,6 +12,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.Mo public sealed class CreateResourceTests : OpenApiClientTests { + private const string DataPropertyName = "Data"; private readonly NrtOnMsvOnFakers _fakers = new(); [Theory] @@ -43,7 +44,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { @@ -88,7 +89,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(attributePropertyName); + CreateAttributeSelectorFor(attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -112,7 +113,7 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] - public async Task Cannot_clear_attribute(string clrPropertyName, string jsonPropertyName) + public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -132,19 +133,18 @@ public async Task Cannot_clear_attribute(string clrPropertyName, string jsonProp } }; - requestDocument.Data.Attributes.SetPropertyValue(clrPropertyName, null); + requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); Expression> includeAttributeSelector = - CreateIncludedAttributeSelector(clrPropertyName); + CreateAttributeSelectorFor(attributePropertyName); using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -236,8 +236,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -249,9 +248,8 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -272,7 +270,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -293,9 +291,8 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), nameof(ResourceRelationshipsInPostRequest.NullableToOne.Data), "nullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] + public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -316,7 +313,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -334,16 +331,12 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -364,7 +357,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -372,8 +365,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -385,16 +377,12 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), nameof(ResourceRelationshipsInPostRequest.NonNullableToOne.Data), - "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne.Data), - "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne.Data), - "requiredNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), nameof(ResourceRelationshipsInPostRequest.ToMany.Data), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), nameof(ResourceRelationshipsInPostRequest.RequiredToMany.Data), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string dataPropertyName, - string jsonPropertyName) + [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] + [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] + public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -415,14 +403,13 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat }; object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(dataPropertyName, null); + relationshipObject!.SetPropertyValue(DataPropertyName, null); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -558,8 +545,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using (apiClient.WithPartialAttributeSerialization(requestDocument)) { // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -602,8 +588,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 29e87db27b..c63672966f 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -42,7 +42,7 @@ public async Task Cannot_exclude_id() var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(999, requestDocument)); + Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); // Assert await action.Should().ThrowAsync(); From 55e702cfdf78523f4ec3c329927bbc1dda684b75 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 01:12:10 +0100 Subject: [PATCH 38/66] Simplify usings --- .../CreateResourceTests.cs | 92 ++++++------- .../UpdateResourceTests.cs | 18 ++- .../CreateResourceTests.cs | 128 ++++++++---------- .../UpdateResourceTests.cs | 18 ++- .../CreateResourceTests.cs | 128 ++++++++---------- .../UpdateResourceTests.cs | 18 ++- .../CreateResourceTests.cs | 128 ++++++++---------- .../UpdateResourceTests.cs | 18 ++- 8 files changed, 251 insertions(+), 297 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index bb39bf475a..d036f1e9af 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -46,11 +46,10 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -91,11 +90,10 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -135,11 +133,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -179,18 +176,17 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Be( - $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); + + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] @@ -220,11 +216,10 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -301,18 +296,17 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -381,11 +375,10 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -424,11 +417,10 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 69b4a37ebf..b0e3ad1b8e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -83,11 +83,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -129,11 +128,10 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 6b2fae129b..9eeaa34a50 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -44,11 +44,10 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -90,11 +89,10 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -133,17 +131,16 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); } [Theory] @@ -176,11 +173,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -219,18 +215,17 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( - $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); - } + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] @@ -259,11 +254,10 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -340,18 +334,17 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -422,11 +415,10 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -466,11 +458,10 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -508,18 +499,17 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 6f02e27b29..74527ec957 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -83,11 +83,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -129,11 +128,10 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index abb201b582..9b0bafd667 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -48,11 +48,10 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -95,11 +94,10 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -141,17 +139,16 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); } [Theory] @@ -186,11 +183,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -233,18 +229,17 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( - $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); - } + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] @@ -276,11 +271,10 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -363,18 +357,17 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -450,11 +443,10 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -496,11 +488,10 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -540,18 +531,17 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index dedaf8f5a9..54890dffd3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -89,11 +89,10 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -139,11 +138,10 @@ public async Task Can_exclude_relationship_that_is_required_in_create_resource(s using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index c2c1993d47..962806c3ee 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -46,11 +46,10 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -94,11 +93,10 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -141,17 +139,16 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js Expression> includeAttributeSelector = CreateAttributeSelectorFor(attributePropertyName); - using (apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); } [Theory] @@ -186,11 +183,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -233,18 +229,17 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( - $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); - } + exception.Message.Should().Be( + $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } [Theory] @@ -275,11 +270,10 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -362,18 +356,17 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } + exception.Message.Should() + .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -450,11 +443,10 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -496,11 +488,10 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -542,18 +533,17 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); + // Act + Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } + // Assert + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + JsonSerializationException exception = assertion.Subject.Single(); + + exception.Message.Should() + .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index c63672966f..6084f09757 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -89,11 +89,10 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -139,11 +138,10 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using (apiClient.WithPartialAttributeSerialization(requestDocument)) - { - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); - } + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); + + // Act + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); From 28bb39bbb2420eec19ea50baf1b5274a53298e36 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 01:15:25 +0100 Subject: [PATCH 39/66] Sync up test names --- .../ModelStateValidationOff/UpdateResourceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 54890dffd3..1623b49a35 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -61,7 +61,7 @@ public async Task Cannot_exclude_id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute_that_is_required_in_create_resource(string attributePropertyName, string jsonPropertyName) + public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -110,7 +110,7 @@ public async Task Can_exclude_attribute_that_is_required_in_create_resource(stri [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_that_is_required_in_create_resource(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument From ff4262e09984f0cd46a3ce4458d70b5d531896f9 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 01:23:40 +0100 Subject: [PATCH 40/66] Fix invalid tests --- .../ModelStateValidationOff/CreateResourceTests.cs | 2 -- .../ModelStateValidationOn/CreateResourceTests.cs | 2 -- .../ModelStateValidationOff/CreateResourceTests.cs | 2 -- .../ModelStateValidationOn/CreateResourceTests.cs | 2 -- 4 files changed, 8 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index d036f1e9af..eafb7d12f6 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -417,8 +417,6 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 9eeaa34a50..28b8f129a4 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -458,8 +458,6 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 9b0bafd667..4c2395581c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -488,8 +488,6 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 962806c3ee..de29d3e256 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -488,8 +488,6 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); From 821d09dfef43f56e565910dd2485cc2241c2def6 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 01:35:41 +0100 Subject: [PATCH 41/66] Fix assertion messages --- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- .../ModelStateValidationOff/CreateResourceTests.cs | 3 ++- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 28b8f129a4..ba2af57fa0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -140,7 +140,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 4c2395581c..521e7bd46b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -148,7 +148,8 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + exception.Message.Should().StartWith($"Cannot write a null value for property '{jsonPropertyName}'."); + exception.Message.Should().EndWith("Path 'data.attributes'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index de29d3e256..0fdfe3fa5b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -148,7 +148,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Contain($"Cannot write a null value for property '{jsonPropertyName}'."); + exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] From 5b71d906a275b4de4e67cb4aba1e8c2e92f49b87 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 01:54:03 +0100 Subject: [PATCH 42/66] Sync up tests --- .../ModelStateValidationOff/CreateResourceTests.cs | 6 +++--- .../ModelStateValidationOn/CreateResourceTests.cs | 6 +++--- .../ModelStateValidationOff/CreateResourceTests.cs | 6 +++--- .../ModelStateValidationOn/CreateResourceTests.cs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index eafb7d12f6..5a3aaeb0aa 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -84,12 +84,12 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index ba2af57fa0..9380c66e3f 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -83,12 +83,12 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 521e7bd46b..330dd0d539 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -88,12 +88,12 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 0fdfe3fa5b..dcb7c1f051 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -87,12 +87,12 @@ public async Task Can_set_default_value_to_attribute(string attributePropertyNam object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); + Expression> includeAttributeSelector = + CreateAttributeSelectorFor(attributePropertyName); + using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act From df9a1b43cf20ffec0d09191ba3b1e285702d4a8b Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 9 Jan 2023 02:05:51 +0100 Subject: [PATCH 43/66] Revert change to pass full options instead of just the naming policy --- .../SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs | 7 +++---- .../SwaggerComponents/ResourceObjectSchemaGenerator.cs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 678229864c..9b31d2c31f 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,4 +1,4 @@ -using JsonApiDotNetCore.Configuration; +using System.Text.Json; using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -34,14 +34,13 @@ internal sealed class ResourceFieldObjectSchemaBuilder private readonly RelationshipTypeFactory _relationshipTypeFactory; public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, - SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, IJsonApiOptions options, + SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, JsonNamingPolicy? namingPolicy, ResourceFieldValidationMetadataProvider resourceFieldValidationMetadataProvider) { ArgumentGuard.NotNull(resourceTypeInfo); ArgumentGuard.NotNull(schemaRepositoryAccessor); ArgumentGuard.NotNull(defaultSchemaGenerator); ArgumentGuard.NotNull(resourceTypeSchemaGenerator); - ArgumentGuard.NotNull(options); ArgumentGuard.NotNull(resourceFieldValidationMetadataProvider); _resourceTypeInfo = resourceTypeInfo; @@ -50,7 +49,7 @@ public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISche _resourceTypeSchemaGenerator = resourceTypeSchemaGenerator; _resourceFieldValidationMetadataProvider = resourceFieldValidationMetadataProvider; - _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); + _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, namingPolicy); _relationshipTypeFactory = new RelationshipTypeFactory(resourceFieldValidationMetadataProvider); _schemasForResourceFields = GetFieldSchemas(); } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 8e061fff57..ffa3729dcf 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -31,7 +31,7 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe _allowClientGeneratedIds = options.AllowClientGeneratedIds; _resourceFieldObjectSchemaBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, - defaultSchemaGenerator, _resourceTypeSchemaGenerator, options, resourceFieldValidationMetadataProvider); + defaultSchemaGenerator, _resourceTypeSchemaGenerator, options.SerializerOptions.PropertyNamingPolicy, resourceFieldValidationMetadataProvider); } public OpenApiSchema GenerateSchema(Type resourceObjectType) From 9844e6abb144b2771bc23c13a335bb401aa9867c Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 10 Jan 2023 22:13:40 +0100 Subject: [PATCH 44/66] Fix casing in test names --- .../ModelStateValidationOff/UpdateResourceTests.cs | 2 +- .../ModelStateValidationOn/UpdateResourceTests.cs | 2 +- .../ModelStateValidationOff/UpdateResourceTests.cs | 2 +- .../ModelStateValidationOn/UpdateResourceTests.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index b0e3ad1b8e..aec1e796a7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -14,7 +14,7 @@ public sealed class UpdateResourceTests private readonly NrtOffMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_exclude_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 74527ec957..165a73829d 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -14,7 +14,7 @@ public sealed class UpdateResourceTests private readonly NrtOffMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_exclude_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 1623b49a35..80f674d97b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -14,7 +14,7 @@ public sealed class UpdateResourceTests private readonly NrtOnMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_exclude_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 6084f09757..4f6b4ae839 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -14,7 +14,7 @@ public sealed class UpdateResourceTests private readonly NrtOnMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_id() + public async Task Cannot_exclude_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument From 6071f5ba0eec298ae724cfab702e03792cfc0903 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 10 Jan 2023 23:47:50 +0100 Subject: [PATCH 45/66] Simplify Cannot_exclude_Id tests --- .../ModelStateValidationOff/UpdateResourceTests.cs | 7 ++----- .../ModelStateValidationOn/UpdateResourceTests.cs | 7 ++----- .../ModelStateValidationOff/UpdateResourceTests.cs | 7 ++----- .../ModelStateValidationOn/UpdateResourceTests.cs | 7 ++----- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index aec1e796a7..aa6830b602 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -21,7 +21,7 @@ public async Task Cannot_exclude_Id() { Data = new ResourceDataInPatchRequest { - Id = "1", + Id = null, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -33,14 +33,11 @@ public async Task Cannot_exclude_Id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 165a73829d..c04903e606 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -21,7 +21,7 @@ public async Task Cannot_exclude_Id() { Data = new ResourceDataInPatchRequest { - Id = "1", + Id = null, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -33,14 +33,11 @@ public async Task Cannot_exclude_Id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 80f674d97b..eeac6ef4d6 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -21,7 +21,7 @@ public async Task Cannot_exclude_Id() { Data = new ResourceDataInPatchRequest { - Id = "1", + Id = null!, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -35,14 +35,11 @@ public async Task Cannot_exclude_Id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert await action.Should().ThrowAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 4f6b4ae839..b232403b71 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -21,7 +21,7 @@ public async Task Cannot_exclude_Id() { Data = new ResourceDataInPatchRequest { - Id = "1", + Id = null!, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -35,14 +35,11 @@ public async Task Cannot_exclude_Id() } }; - ResourceDataInPatchRequest emptyDataObject = new(); - requestDocument.Data.Id = emptyDataObject.Id; - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(999, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert await action.Should().ThrowAsync(); From 688120834cc36ad00f327e0f8eaea47076f8d579 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 11 Jan 2023 00:04:33 +0100 Subject: [PATCH 46/66] Rename base type to avoid OpenApiClientTests.OpenApiClientTests --- .../{OpenApiClientTests.cs => BaseOpenApiClientTests.cs} | 2 +- .../ModelStateValidationOff/CreateResourceTests.cs | 2 +- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- .../ModelStateValidationOff/CreateResourceTests.cs | 2 +- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename test/OpenApiClientTests/{OpenApiClientTests.cs => BaseOpenApiClientTests.cs} (94%) diff --git a/test/OpenApiClientTests/OpenApiClientTests.cs b/test/OpenApiClientTests/BaseOpenApiClientTests.cs similarity index 94% rename from test/OpenApiClientTests/OpenApiClientTests.cs rename to test/OpenApiClientTests/BaseOpenApiClientTests.cs index 8b1987f5b1..20e7a41af4 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.cs +++ b/test/OpenApiClientTests/BaseOpenApiClientTests.cs @@ -2,7 +2,7 @@ namespace OpenApiClientTests; -public abstract class OpenApiClientTests +public abstract class BaseOpenApiClientTests { private const string AttributesObjectParameterName = "attributesObject"; diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 5a3aaeb0aa..b4e538a562 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { private const string DataPropertyName = "Data"; private readonly NrtOffMsvOffFakers _fakers = new(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 9380c66e3f..44709d0cf8 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { private const string DataPropertyName = "Data"; private readonly NrtOffMsvOnFakers _fakers = new(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 330dd0d539..9134e46db7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { private const string DataPropertyName = "Data"; private readonly NrtOnMsvOffFakers _fakers = new(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index dcb7c1f051..167855b7e5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -10,7 +10,7 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class CreateResourceTests : OpenApiClientTests +public sealed class CreateResourceTests : BaseOpenApiClientTests { private const string DataPropertyName = "Data"; private readonly NrtOnMsvOnFakers _fakers = new(); From cc3815cb3b9bee8b7249502b3e7f857d52925b24 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 30 Sep 2023 15:32:39 +0200 Subject: [PATCH 47/66] Adapt to existing naming convention --- .../ModelStateValidationOff/RequiredTests.cs | 4 ++-- .../ModelStateValidationOn/RequiredTests.cs | 4 ++-- .../ModelStateValidationOff/RequiredTests.cs | 4 ++-- .../ModelStateValidationOn/RequiredTests.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs index e4bd230596..6974a721c2 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/RequiredTests.cs @@ -82,7 +82,7 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ } [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -92,7 +92,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso } [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs index f07cb7b2cb..bab06b8b23 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/RequiredTests.cs @@ -82,7 +82,7 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ } [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -92,7 +92,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso } [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs index 7dfd5e7ef2..7020004021 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/RequiredTests.cs @@ -86,7 +86,7 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ } [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -96,7 +96,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso } [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs index 6d7c4a5420..e33a4ae3d7 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/RequiredTests.cs @@ -86,7 +86,7 @@ public async Task Schema_property_for_relationship_is_not_required_for_creating_ } [Fact] - public async Task No_attribute_schema_properties_are_required_when_updating_resource() + public async Task No_attribute_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); @@ -96,7 +96,7 @@ public async Task No_attribute_schema_properties_are_required_when_updating_reso } [Fact] - public async Task No_relationship_schema_properties_are_required_when_updating_resource() + public async Task No_relationship_schema_properties_are_required_for_updating_resource() { // Act JsonElement document = await _testContext.GetSwaggerDocumentAsync(); From 4ef821462b6307cf9a9acbf9d6259fd7ac13296e Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 30 Sep 2023 15:59:16 +0200 Subject: [PATCH 48/66] Remove redundant assertions, fix formatting --- .../CreateResourceTests.cs | 8 ++++---- .../UpdateResourceTests.cs | 1 - .../CreateResourceTests.cs | 16 ++++++++-------- .../UpdateResourceTests.cs | 1 - .../CreateResourceTests.cs | 16 ++++++++-------- .../UpdateResourceTests.cs | 1 - .../CreateResourceTests.cs | 16 ++++++++-------- .../UpdateResourceTests.cs | 1 - 8 files changed, 28 insertions(+), 32 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index b4e538a562..ec78cd8507 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -305,8 +305,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -343,8 +343,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index aa6830b602..5dd7411d3a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -40,7 +40,6 @@ public async Task Cannot_exclude_Id() Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 44709d0cf8..8b7cebecfa 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -343,8 +343,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -382,8 +382,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -506,8 +506,8 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] @@ -544,7 +544,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index c04903e606..8865a57d5e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -40,7 +40,6 @@ public async Task Cannot_exclude_Id() Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 9134e46db7..5976ff1038 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -367,8 +367,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -409,8 +409,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -539,8 +539,8 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] @@ -579,7 +579,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index eeac6ef4d6..805509570d 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -42,7 +42,6 @@ public async Task Cannot_exclude_Id() Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 167855b7e5..2b99139dbb 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -365,8 +365,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -408,8 +408,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } [Theory] @@ -540,8 +540,8 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } [Theory] @@ -582,7 +582,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should() - .Be($"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); + exception.Message.Should().Be( + $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index b232403b71..c71406f816 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -42,7 +42,6 @@ public async Task Cannot_exclude_Id() Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); // Assert - await action.Should().ThrowAsync(); ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); JsonSerializationException exception = assertion.Subject.Single(); From dcdf5a5d0c702608f726106fc6ce18100ee91baf Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 30 Sep 2023 23:22:39 +0200 Subject: [PATCH 49/66] Correct test names --- .../ModelStateValidationOff/CreateResourceTests.cs | 2 +- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- .../ModelStateValidationOff/CreateResourceTests.cs | 2 +- .../ModelStateValidationOn/CreateResourceTests.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index ec78cd8507..0de8fc4002 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -63,7 +63,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 8b7cebecfa..588a671f1c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -62,7 +62,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 5976ff1038..d9f8daeb82 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -65,7 +65,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 2b99139dbb..1df7eff7df 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -64,7 +64,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_set_default_value_to_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument From 2d7f8b9997df809bdbdc2bf3a2457a5271503bfa Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 02:25:09 +0200 Subject: [PATCH 50/66] Centralize code for property assignment in tests --- .../BaseOpenApiClientTests.cs | 66 +++++++++++++++++++ test/OpenApiClientTests/ObjectExtensions.cs | 9 --- .../CreateResourceTests.cs | 42 ++++-------- .../UpdateResourceTests.cs | 17 ++--- .../CreateResourceTests.cs | 56 ++++++---------- .../UpdateResourceTests.cs | 17 ++--- .../CreateResourceTests.cs | 56 ++++++---------- .../UpdateResourceTests.cs | 17 ++--- .../CreateResourceTests.cs | 56 ++++++---------- .../UpdateResourceTests.cs | 17 ++--- 10 files changed, 161 insertions(+), 192 deletions(-) diff --git a/test/OpenApiClientTests/BaseOpenApiClientTests.cs b/test/OpenApiClientTests/BaseOpenApiClientTests.cs index 20e7a41af4..a8ba84de9a 100644 --- a/test/OpenApiClientTests/BaseOpenApiClientTests.cs +++ b/test/OpenApiClientTests/BaseOpenApiClientTests.cs @@ -1,4 +1,6 @@ using System.Linq.Expressions; +using System.Reflection; +using JsonApiDotNetCore.OpenApi.Client; namespace OpenApiClientTests; @@ -17,4 +19,68 @@ public abstract class BaseOpenApiClientTests return Expression.Lambda>(toObjectConversion, parameter); } + + /// + /// Sets the property on the specified source to its default value (null for string, 0 for int, false for bool, etc). + /// + protected static void SetPropertyToDefaultValue(T source, string propertyName) + where T : class + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + PropertyInfo property = GetExistingProperty(typeof(T), propertyName); + + object? defaultValue = property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; + property.SetValue(source, defaultValue); + } + + /// + /// Sets the property on the specified source to its initial value, when the type was constructed. This takes the presence of a type initializer into + /// account. + /// + protected static void SetPropertyToInitialValue(T source, string propertyName) + where T : class, new() + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(propertyName); + + var emptyRelationshipsObject = new T(); + object? defaultValue = emptyRelationshipsObject.GetPropertyValue(propertyName); + + source.SetPropertyValue(propertyName, defaultValue); + } + + /// + /// Sets the 'Data' property of the specified relationship to null. + /// + protected static void SetDataPropertyToNull(T source, string relationshipPropertyName) + where T : class + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(relationshipPropertyName); + + PropertyInfo relationshipProperty = GetExistingProperty(typeof(T), relationshipPropertyName); + object? relationshipValue = relationshipProperty.GetValue(source); + + if (relationshipValue == null) + { + throw new InvalidOperationException($"Property '{typeof(T).Name}.{relationshipPropertyName}' is null."); + } + + PropertyInfo dataProperty = GetExistingProperty(relationshipProperty.PropertyType, "Data"); + dataProperty.SetValue(relationshipValue, null); + } + + private static PropertyInfo GetExistingProperty(Type type, string propertyName) + { + PropertyInfo? property = type.GetProperty(propertyName); + + if (property == null) + { + throw new InvalidOperationException($"Type '{type.Name}' does not contain a property named '{propertyName}'."); + } + + return property; + } } diff --git a/test/OpenApiClientTests/ObjectExtensions.cs b/test/OpenApiClientTests/ObjectExtensions.cs index c98e0fe5de..3f2633f5ff 100644 --- a/test/OpenApiClientTests/ObjectExtensions.cs +++ b/test/OpenApiClientTests/ObjectExtensions.cs @@ -23,15 +23,6 @@ public static void SetPropertyValue(this object source, string propertyName, obj propertyInfo.SetValue(source, value); } - public static object? GetDefaultValueForProperty(this object source, string propertyName) - { - ArgumentGuard.NotNull(source); - ArgumentGuard.NotNull(propertyName); - - PropertyInfo propertyInfo = GetExistingProperty(source.GetType(), propertyName); - return propertyInfo.PropertyType.IsValueType ? Activator.CreateInstance(propertyInfo.PropertyType) : null; - } - private static PropertyInfo GetExistingProperty(Type type, string propertyName) { PropertyInfo? propertyInfo = type.GetProperty(propertyName); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 0de8fc4002..ef5ed004e0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -12,7 +12,6 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.M public sealed class CreateResourceTests : BaseOpenApiClientTests { - private const string DataPropertyName = "Data"; private readonly NrtOffMsvOffFakers _fakers = new(); [Theory] @@ -38,7 +37,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -81,8 +80,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -108,7 +106,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -126,9 +124,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -151,7 +147,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -169,9 +165,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -210,8 +204,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -251,8 +244,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -290,8 +282,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -330,8 +321,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -350,7 +340,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -368,9 +358,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -392,7 +380,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -410,9 +398,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 5dd7411d3a..0429245150 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -9,19 +9,18 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { private readonly NrtOffMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_Id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = null, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -53,7 +52,7 @@ public async Task Cannot_exclude_Id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -72,9 +71,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -98,7 +95,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -117,9 +114,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 588a671f1c..806eb82d4a 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -12,7 +12,6 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.M public sealed class CreateResourceTests : BaseOpenApiClientTests { - private const string DataPropertyName = "Data"; private readonly NrtOffMsvOnFakers _fakers = new(); [Theory] @@ -36,7 +35,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -80,8 +79,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -123,7 +121,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -148,7 +146,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -166,9 +164,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -190,7 +186,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -208,9 +204,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -248,8 +242,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -288,8 +281,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -328,8 +320,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -369,8 +360,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -390,7 +380,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -408,9 +398,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -433,7 +421,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -451,9 +439,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -472,7 +458,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -490,9 +476,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -512,7 +496,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -530,9 +514,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 8865a57d5e..41ce44c4e0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -9,19 +9,18 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { private readonly NrtOffMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_Id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = null, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -53,7 +52,7 @@ public async Task Cannot_exclude_Id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -72,9 +71,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -98,7 +95,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToOne), "requiredToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -117,9 +114,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index d9f8daeb82..c3b777b726 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -12,7 +12,6 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.Mo public sealed class CreateResourceTests : BaseOpenApiClientTests { - private const string DataPropertyName = "Data"; private readonly NrtOnMsvOffFakers _fakers = new(); [Theory] @@ -40,7 +39,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -85,8 +84,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -131,7 +129,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -157,7 +155,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -177,9 +175,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -203,7 +199,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -223,9 +219,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -266,8 +260,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -309,8 +302,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -352,8 +344,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -396,8 +387,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -417,7 +407,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -437,9 +427,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -462,7 +450,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -482,9 +470,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -503,7 +489,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -523,9 +509,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -545,7 +529,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -565,9 +549,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 805509570d..7685c56bc5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -9,19 +9,18 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { private readonly NrtOnMsvOffFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_Id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = null!, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -57,7 +56,7 @@ public async Task Cannot_exclude_Id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -78,9 +77,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -106,7 +103,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -127,9 +124,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 1df7eff7df..1685abd005 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -12,7 +12,6 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.Mo public sealed class CreateResourceTests : BaseOpenApiClientTests { - private const string DataPropertyName = "Data"; private readonly NrtOnMsvOnFakers _fakers = new(); [Theory] @@ -38,7 +37,7 @@ public async Task Can_clear_attribute(string attributePropertyName, string jsonP } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -84,8 +83,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - object? defaultValue = requestDocument.Data.Attributes.GetDefaultValueForProperty(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -131,7 +129,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js } }; - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, null); + SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -156,7 +154,7 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -176,9 +174,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -202,7 +198,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Cannot_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -222,9 +218,7 @@ public async Task Cannot_exclude_attribute(string attributePropertyName, string } }; - ResourceAttributesInPostRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -264,8 +258,7 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -306,8 +299,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -350,8 +342,7 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -395,8 +386,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat } }; - object? relationshipObject = requestDocument.Data.Relationships.GetPropertyValue(relationshipPropertyName); - relationshipObject!.SetPropertyValue(DataPropertyName, null); + SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -416,7 +406,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -436,9 +426,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -461,7 +449,7 @@ public async Task Can_exclude_relationship_with_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -481,9 +469,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -504,7 +490,7 @@ public async Task Can_exclude_relationship_without_partial_attribute_serializati [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_exclude_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -524,9 +510,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -548,7 +532,7 @@ public async Task Cannot_exclude_relationship_with_partial_attribute_serializati [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_exclude_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -568,9 +552,7 @@ public async Task Cannot_exclude_relationship_without_partial_attribute_serializ } }; - ResourceRelationshipsInPostRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index c71406f816..d1dc00b8e8 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -9,19 +9,18 @@ namespace OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn; -public sealed class UpdateResourceTests +public sealed class UpdateResourceTests : BaseOpenApiClientTests { private readonly NrtOnMsvOnFakers _fakers = new(); [Fact] - public async Task Cannot_exclude_Id() + public async Task Cannot_omit_Id() { // Arrange var requestDocument = new ResourcePatchRequestDocument { Data = new ResourceDataInPatchRequest { - Id = null!, Attributes = _fakers.PatchAttributes.Generate(), Relationships = new ResourceRelationshipsInPatchRequest { @@ -57,7 +56,7 @@ public async Task Cannot_exclude_Id() [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPatchRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_exclude_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Can_omit_attribute(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -78,9 +77,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso } }; - ResourceAttributesInPatchRequest emptyAttributesObject = new(); - object? defaultValue = emptyAttributesObject.GetPropertyValue(attributePropertyName); - requestDocument.Data.Attributes.SetPropertyValue(attributePropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -106,7 +103,7 @@ public async Task Can_exclude_attribute(string attributePropertyName, string jso [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPatchRequest.RequiredToMany), "requiredToMany")] - public async Task Can_exclude_relationship(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePatchRequestDocument @@ -127,9 +124,7 @@ public async Task Can_exclude_relationship(string relationshipPropertyName, stri } }; - ResourceRelationshipsInPatchRequest emptyRelationshipsObject = new(); - object? defaultValue = emptyRelationshipsObject.GetPropertyValue(relationshipPropertyName); - requestDocument.Data.Relationships.SetPropertyValue(relationshipPropertyName, defaultValue); + SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); From 41d6d678cb0331c33aed543566452335fa15bbf4 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:35:31 +0200 Subject: [PATCH 51/66] Apply Resharper hint: convert switch statement to expression --- .../JsonApiEndpointMetadataProvider.cs | 55 +++++-------------- .../OpenApiEndpointConvention.cs | 55 ++++++------------- test/OpenApiClientTests/FakerFactory.cs | 21 ++----- 3 files changed, 37 insertions(+), 94 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs index 0dcb87cabb..303fa959cc 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs @@ -50,27 +50,14 @@ public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction) private IJsonApiRequestMetadata? GetRequestMetadata(JsonApiEndpoint endpoint, ResourceType primaryResourceType) { - switch (endpoint) + return endpoint switch { - case JsonApiEndpoint.Post: - { - return GetPostRequestMetadata(primaryResourceType.ClrType); - } - case JsonApiEndpoint.Patch: - { - return GetPatchRequestMetadata(primaryResourceType.ClrType); - } - case JsonApiEndpoint.PostRelationship: - case JsonApiEndpoint.PatchRelationship: - case JsonApiEndpoint.DeleteRelationship: - { - return GetRelationshipRequestMetadata(primaryResourceType.Relationships, endpoint != JsonApiEndpoint.PatchRelationship); - } - default: - { - return null; - } - } + JsonApiEndpoint.Post => GetPostRequestMetadata(primaryResourceType.ClrType), + JsonApiEndpoint.Patch => GetPatchRequestMetadata(primaryResourceType.ClrType), + JsonApiEndpoint.PostRelationship or JsonApiEndpoint.PatchRelationship or JsonApiEndpoint.DeleteRelationship => GetRelationshipRequestMetadata( + primaryResourceType.Relationships, endpoint != JsonApiEndpoint.PatchRelationship), + _ => null + }; } private static PrimaryRequestMetadata GetPostRequestMetadata(Type resourceClrType) @@ -99,28 +86,14 @@ private RelationshipRequestMetadata GetRelationshipRequestMetadata(IEnumerable GetPrimaryResponseMetadata( + primaryResourceType.ClrType, endpoint == JsonApiEndpoint.GetCollection), + JsonApiEndpoint.GetSecondary => GetSecondaryResponseMetadata(primaryResourceType.Relationships), + JsonApiEndpoint.GetRelationship => GetRelationshipResponseMetadata(primaryResourceType.Relationships), + _ => null + }; } private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type resourceClrType, bool endpointReturnsCollection) diff --git a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs index 1ec0d0dbcf..09c5b8c650 100644 --- a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs +++ b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs @@ -105,49 +105,28 @@ private void SetResponseMetadata(ActionModel action, JsonApiEndpoint endpoint) private static IList GetStatusCodesForEndpoint(JsonApiEndpoint endpoint) { - switch (endpoint) + return endpoint switch { - case JsonApiEndpoint.GetCollection: - case JsonApiEndpoint.GetSingle: - case JsonApiEndpoint.GetSecondary: - case JsonApiEndpoint.GetRelationship: + JsonApiEndpoint.GetCollection or JsonApiEndpoint.GetSingle or JsonApiEndpoint.GetSecondary or JsonApiEndpoint.GetRelationship => new[] { - return new[] - { - StatusCodes.Status200OK - }; - } - case JsonApiEndpoint.Post: + StatusCodes.Status200OK + }, + JsonApiEndpoint.Post => new[] { - return new[] - { - StatusCodes.Status201Created, - StatusCodes.Status204NoContent - }; - } - case JsonApiEndpoint.Patch: + StatusCodes.Status201Created, + StatusCodes.Status204NoContent + }, + JsonApiEndpoint.Patch => new[] { - return new[] - { - StatusCodes.Status200OK, - StatusCodes.Status204NoContent - }; - } - case JsonApiEndpoint.Delete: - case JsonApiEndpoint.PostRelationship: - case JsonApiEndpoint.PatchRelationship: - case JsonApiEndpoint.DeleteRelationship: + StatusCodes.Status200OK, + StatusCodes.Status204NoContent + }, + JsonApiEndpoint.Delete or JsonApiEndpoint.PostRelationship or JsonApiEndpoint.PatchRelationship or JsonApiEndpoint.DeleteRelationship => new[] { - return new[] - { - StatusCodes.Status204NoContent - }; - } - default: - { - throw new UnreachableCodeException(); - } - } + StatusCodes.Status204NoContent + }, + _ => throw new UnreachableCodeException() + }; } private static void SetRequestMetadata(ActionModel action, JsonApiEndpoint endpoint) diff --git a/test/OpenApiClientTests/FakerFactory.cs b/test/OpenApiClientTests/FakerFactory.cs index db4fe3a406..74b6a81223 100644 --- a/test/OpenApiClientTests/FakerFactory.cs +++ b/test/OpenApiClientTests/FakerFactory.cs @@ -54,22 +54,13 @@ public override void Generate(AutoGenerateOverrideContext context) private static object ToPositiveValue(object idValue) { - if (idValue is short shortValue) + return idValue switch { - return Math.Abs(shortValue); - } - - if (idValue is int intValue) - { - return Math.Abs(intValue); - } - - if (idValue is long longValue) - { - return Math.Abs(longValue); - } - - return idValue; + short shortValue => Math.Abs(shortValue), + int intValue => Math.Abs(intValue), + long longValue => Math.Abs(longValue), + _ => idValue + }; } [UsedImplicitly(ImplicitUseTargetFlags.Members)] From bd31216c585300df0ae2338a1e9647b400e9b627 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:36:31 +0200 Subject: [PATCH 52/66] Simplify expressions --- src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs | 7 +++---- .../ActionDescriptorExtensions.cs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs index 1c5bdec559..4702c8af7c 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/JsonApiClient.cs @@ -280,9 +280,9 @@ private void AssertRequiredAttributesHaveNonDefaultValues(object attributesObjec private static void AssertPropertyHasNonDefaultValueIfRequired(object attributesObject, PropertyInfo propertyInfo, string jsonPath) { - JsonPropertyAttribute jsonProperty = propertyInfo.GetCustomAttributes().Single(); + var jsonProperty = propertyInfo.GetCustomAttribute(); - if (jsonProperty.Required is Required.Always or Required.AllowNull) + if (jsonProperty is { Required: Required.Always or Required.AllowNull }) { bool propertyHasDefaultValue = PropertyHasDefaultValue(propertyInfo, attributesObject); @@ -311,8 +311,7 @@ private static bool PropertyHasDefaultValue(PropertyInfo propertyInfo, object in /// /// Corrects the and JSON annotations at runtime, which appear on the auto-generated /// properties for JSON:API attributes. For example: - /// - /// /// diff --git a/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs index 024891e8ab..772eed3c1d 100644 --- a/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ActionDescriptorExtensions.cs @@ -21,7 +21,7 @@ public static MethodInfo GetActionMethod(this ActionDescriptor descriptor) ArgumentGuard.NotNull(descriptor); IFilterMetadata? filterMetadata = descriptor.FilterDescriptors.Select(filterDescriptor => filterDescriptor.Filter) - .FirstOrDefault(filter => filter is TFilterMetaData); + .OfType().FirstOrDefault(); return (TFilterMetaData?)filterMetadata; } From 6a877519eeb6197e0727aabaeffac66149f92ade Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:40:42 +0200 Subject: [PATCH 53/66] Simplify exception assertions --- .../LegacyClient/ResponseTests.cs | 5 ++--- .../CreateResourceTests.cs | 9 +++------ .../UpdateResourceTests.cs | 3 +-- .../CreateResourceTests.cs | 18 ++++++----------- .../UpdateResourceTests.cs | 3 +-- .../CreateResourceTests.cs | 20 +++++++------------ .../UpdateResourceTests.cs | 3 +-- .../CreateResourceTests.cs | 18 ++++++----------- .../UpdateResourceTests.cs | 3 +-- 9 files changed, 28 insertions(+), 54 deletions(-) diff --git a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs index 6a92e218df..78ffc87a76 100644 --- a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs @@ -216,10 +216,9 @@ public async Task Getting_unknown_resource_translates_error_response() // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - ApiException exception = assertion.Subject.Single(); - exception.StatusCode.Should().Be((int)HttpStatusCode.NotFound); - exception.Response.Should().Be(responseBody); + assertion.Which.StatusCode.Should().Be((int)HttpStatusCode.NotFound); + assertion.Which.Response.Should().Be(responseBody); } [Fact] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index ef5ed004e0..90f846742b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -177,9 +177,8 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } @@ -294,9 +293,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -331,9 +329,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 0429245150..9e87362b11 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -40,9 +40,8 @@ public async Task Cannot_omit_Id() // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + assertion.Which.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 806eb82d4a..46c578c1e5 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -136,9 +136,8 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); + assertion.Which.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] @@ -216,9 +215,8 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } @@ -332,9 +330,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -370,9 +367,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -488,9 +484,8 @@ public async Task Cannot_omit_relationship_with_partial_attribute_serialization( // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } @@ -524,9 +519,8 @@ public async Task Cannot_omit_relationship_without_partial_attribute_serializati // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 41ce44c4e0..cad993df00 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -40,9 +40,8 @@ public async Task Cannot_omit_Id() // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + assertion.Which.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index c3b777b726..1ab6bd3a80 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -144,10 +144,9 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().StartWith($"Cannot write a null value for property '{jsonPropertyName}'."); - exception.Message.Should().EndWith("Path 'data.attributes'."); + assertion.Which.Message.Should().StartWith($"Cannot write a null value for property '{jsonPropertyName}'."); + assertion.Which.Message.Should().EndWith("Path 'data.attributes'."); } [Theory] @@ -231,9 +230,8 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } @@ -356,9 +354,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -397,9 +394,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -521,9 +517,8 @@ public async Task Cannot_omit_relationship_with_partial_attribute_serialization( // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } @@ -559,9 +554,8 @@ public async Task Cannot_omit_relationship_without_partial_attribute_serializati // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 7685c56bc5..1fe393e225 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -42,9 +42,8 @@ public async Task Cannot_omit_Id() // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + assertion.Which.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); } [Theory] diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 1685abd005..c915e44635 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -144,9 +144,8 @@ public async Task Cannot_clear_attribute(string attributePropertyName, string js // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); + assertion.Which.Message.Should().Be($"Cannot write a null value for property '{jsonPropertyName}'. Property requires a value. Path 'data.attributes'."); } [Theory] @@ -230,9 +229,8 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - InvalidOperationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Required property '{attributePropertyName}' at JSON path 'data.attributes.{jsonPropertyName}' is not set. If sending its default value is intended, include it explicitly."); } @@ -354,9 +352,8 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -396,9 +393,8 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } @@ -522,9 +518,8 @@ public async Task Cannot_omit_relationship_with_partial_attribute_serialization( // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } @@ -562,9 +557,8 @@ public async Task Cannot_omit_relationship_without_partial_attribute_serializati // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be( + assertion.Which.Message.Should().Be( $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); } } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index d1dc00b8e8..99b95bf578 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -42,9 +42,8 @@ public async Task Cannot_omit_Id() // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - JsonSerializationException exception = assertion.Subject.Single(); - exception.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); + assertion.Which.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); } [Theory] From 5c85c3ace6b8530f0a9fd2bd4ffe7d851c4f2fe3 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:48:57 +0200 Subject: [PATCH 54/66] Use string interpolation --- .../JsonApiOperationIdSelector.cs | 6 ++-- .../EndpointOrderingFilter.cs | 4 +-- .../LegacyClient/RequestTests.cs | 30 +++++++++---------- .../LegacyClient/ResponseTests.cs | 16 +++++----- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs index 215ea8c24a..a32d781f5f 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs @@ -13,9 +13,9 @@ namespace JsonApiDotNetCore.OpenApi; internal sealed class JsonApiOperationIdSelector { private const string ResourceOperationIdTemplate = "[Method] [PrimaryResourceName]"; - private const string ResourceCollectionOperationIdTemplate = ResourceOperationIdTemplate + " Collection"; - private const string SecondaryOperationIdTemplate = ResourceOperationIdTemplate + " [RelationshipName]"; - private const string RelationshipOperationIdTemplate = SecondaryOperationIdTemplate + " Relationship"; + private const string ResourceCollectionOperationIdTemplate = $"{ResourceOperationIdTemplate} Collection"; + private const string SecondaryOperationIdTemplate = $"{ResourceOperationIdTemplate} [RelationshipName]"; + private const string RelationshipOperationIdTemplate = $"{SecondaryOperationIdTemplate} Relationship"; private static readonly IDictionary DocumentOpenTypeToOperationIdTemplateMap = new Dictionary { diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs index 4de3beffe8..7740d2973b 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/EndpointOrderingFilter.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class EndpointOrderingFilter : IDocumentFilter { private static readonly Regex RelationshipNameInUrlPattern = - new($".*{JsonApiRoutingTemplate.PrimaryEndpoint}/(?>{JsonApiRoutingTemplate.RelationshipsPart}\\/)?(\\w+)", RegexOptions.Compiled); + new($@".*{JsonApiRoutingTemplate.PrimaryEndpoint}/(?>{JsonApiRoutingTemplate.RelationshipsPart}\/)?(\w+)", RegexOptions.Compiled); public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { @@ -34,7 +34,7 @@ private static string GetPrimaryResourcePublicName(KeyValuePair entry) { - return entry.Key.Contains("/" + JsonApiRoutingTemplate.RelationshipsPart); + return entry.Key.Contains($"/{JsonApiRoutingTemplate.RelationshipsPart}"); } private static string GetRelationshipName(KeyValuePair entry) diff --git a/test/OpenApiClientTests/LegacyClient/RequestTests.cs b/test/OpenApiClientTests/LegacyClient/RequestTests.cs index 80a4b9a8e3..628106f4d1 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestTests.cs @@ -28,7 +28,7 @@ public async Task Getting_resource_collection_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + "flights"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights"); wrapper.RequestBody.Should().BeNull(); } @@ -48,7 +48,7 @@ public async Task Getting_resource_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}"); wrapper.RequestBody.Should().BeNull(); } @@ -93,7 +93,7 @@ public async Task Partial_posting_resource_with_selected_relationships_produces_ wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HostPrefix + "flights"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -137,7 +137,7 @@ public async Task Partial_posting_resource_produces_expected_request() capitalLWithStroke }); - string name = "anAirplaneName " + specialCharacters; + string name = $"anAirplaneName {specialCharacters}"; var requestDocument = new AirplanePostRequestDocument { @@ -163,7 +163,7 @@ public async Task Partial_posting_resource_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HostPrefix + "airplanes"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}airplanes"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -214,7 +214,7 @@ public async Task Partial_patching_resource_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"airplanes/{airplaneId}"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}airplanes/{airplaneId}"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -248,7 +248,7 @@ public async Task Deleting_resource_produces_expected_request() // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Method.Should().Be(HttpMethod.Delete); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}"); wrapper.RequestBody.Should().BeNull(); } @@ -268,7 +268,7 @@ public async Task Getting_secondary_resource_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/purser"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/purser"); wrapper.RequestBody.Should().BeNull(); } @@ -288,7 +288,7 @@ public async Task Getting_secondary_resources_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/cabin-crew-members"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/cabin-crew-members"); wrapper.RequestBody.Should().BeNull(); } @@ -308,7 +308,7 @@ public async Task Getting_ToOne_relationship_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/purser"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/purser"); wrapper.RequestBody.Should().BeNull(); } @@ -336,7 +336,7 @@ public async Task Patching_ToOne_relationship_produces_expected_request() // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/purser"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/purser"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -365,7 +365,7 @@ public async Task Getting_ToMany_relationship_produces_expected_request() wrapper.Request.ShouldNotBeNull(); wrapper.Request.Headers.GetValue(HeaderNames.Accept).Should().Be(HeaderConstants.MediaType); wrapper.Request.Method.Should().Be(HttpMethod.Get); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/cabin-crew-members"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/cabin-crew-members"); wrapper.RequestBody.Should().BeNull(); } @@ -401,7 +401,7 @@ public async Task Posting_ToMany_relationship_produces_expected_request() // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Method.Should().Be(HttpMethod.Post); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/cabin-crew-members"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/cabin-crew-members"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -452,7 +452,7 @@ public async Task Patching_ToMany_relationship_produces_expected_request() // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Method.Should().Be(HttpMethod.Patch); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/cabin-crew-members"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/cabin-crew-members"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); @@ -503,7 +503,7 @@ public async Task Deleting_ToMany_relationship_produces_expected_request() // Assert wrapper.Request.ShouldNotBeNull(); wrapper.Request.Method.Should().Be(HttpMethod.Delete); - wrapper.Request.RequestUri.Should().Be(HostPrefix + $"flights/{flightId}/relationships/cabin-crew-members"); + wrapper.Request.RequestUri.Should().Be($"{HostPrefix}flights/{flightId}/relationships/cabin-crew-members"); wrapper.Request.Content.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType.Should().NotBeNull(); wrapper.Request.Content!.Headers.ContentType!.ToString().Should().Be(HeaderConstants.MediaType); diff --git a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs index 78ffc87a76..73f1a9d0e5 100644 --- a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs @@ -25,8 +25,8 @@ public async Task Getting_resource_collection_translates_response() const string purserMetaValue = "https://api.jsonapi.net/docs/#get-flight-purser"; const string cabinCrewMembersMetaValue = "https://api.jsonapi.net/docs/#get-flight-cabin-crew-members"; const string passengersMetaValue = "https://api.jsonapi.net/docs/#get-flight-passengers"; - const string topLevelLink = HostPrefix + "flights"; - const string flightResourceLink = topLevelLink + "/" + flightId; + const string topLevelLink = $"{HostPrefix}flights"; + const string flightResourceLink = $"{topLevelLink}/{flightId}"; const string responseBody = @"{ ""meta"": { @@ -125,20 +125,20 @@ public async Task Getting_resource_collection_translates_response() flight.Attributes.ArrivesAt.Should().BeNull(); flight.Relationships.Purser.Data.Should().BeNull(); - flight.Relationships.Purser.Links.Self.Should().Be(flightResourceLink + "/relationships/purser"); - flight.Relationships.Purser.Links.Related.Should().Be(flightResourceLink + "/purser"); + flight.Relationships.Purser.Links.Self.Should().Be($"{flightResourceLink}/relationships/purser"); + flight.Relationships.Purser.Links.Related.Should().Be($"{flightResourceLink}/purser"); flight.Relationships.Purser.Meta.Should().HaveCount(1); flight.Relationships.Purser.Meta["docs"].Should().Be(purserMetaValue); flight.Relationships.CabinCrewMembers.Data.Should().BeNull(); - flight.Relationships.CabinCrewMembers.Links.Self.Should().Be(flightResourceLink + "/relationships/cabin-crew-members"); - flight.Relationships.CabinCrewMembers.Links.Related.Should().Be(flightResourceLink + "/cabin-crew-members"); + flight.Relationships.CabinCrewMembers.Links.Self.Should().Be($"{flightResourceLink}/relationships/cabin-crew-members"); + flight.Relationships.CabinCrewMembers.Links.Related.Should().Be($"{flightResourceLink}/cabin-crew-members"); flight.Relationships.CabinCrewMembers.Meta.Should().HaveCount(1); flight.Relationships.CabinCrewMembers.Meta["docs"].Should().Be(cabinCrewMembersMetaValue); flight.Relationships.Passengers.Data.Should().BeNull(); - flight.Relationships.Passengers.Links.Self.Should().Be(flightResourceLink + "/relationships/passengers"); - flight.Relationships.Passengers.Links.Related.Should().Be(flightResourceLink + "/passengers"); + flight.Relationships.Passengers.Links.Self.Should().Be($"{flightResourceLink}/relationships/passengers"); + flight.Relationships.Passengers.Links.Related.Should().Be($"{flightResourceLink}/passengers"); flight.Relationships.Passengers.Meta.Should().HaveCount(1); flight.Relationships.Passengers.Meta["docs"].Should().Be(passengersMetaValue); } From b08ab9bacdec304476cc5b36660103d4d1bc77bb Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 17:52:49 +0200 Subject: [PATCH 55/66] Corrections in openapi documentation --- docs/usage/openapi-client.md | 23 ++++++++++++++--------- docs/usage/openapi.md | 7 ++++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md index 7250bb55ce..280ae8ede3 100644 --- a/docs/usage/openapi-client.md +++ b/docs/usage/openapi-client.md @@ -16,7 +16,9 @@ The easiest way to get started is by using the built-in capabilities of Visual S Optionally provide a class name and namespace and click **Finish**. Visual Studio now downloads your swagger.json and updates your project file. This results in a pre-build step that generates the client code. - Tip: To later re-download swagger.json and regenerate the client code, right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon. + > [!TIP] + > To later re-download swagger.json and regenerate the client code, right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon. + 3. Although not strictly required, we recommend to run package update now, which fixes some issues and removes the `Stream` parameter from generated calls. 4. Add some demo code that calls one of your JSON:API endpoints. For example: @@ -41,7 +43,7 @@ The easiest way to get started is by using the built-in capabilities of Visual S dotnet add package JsonApiDotNetCore.OpenApi.Client ``` -6. Add the following glue code to connect our package with your generated code. The code below assumes you specified `ExampleApiClient` as class name in step 2. +6. Add the following glue code to connect our package with your generated code. The code below assumes you specified `ExampleApiClient` as the class name in step 2. ```c# using JsonApiDotNetCore.OpenApi.Client; @@ -56,6 +58,9 @@ The easiest way to get started is by using the built-in capabilities of Visual S } ``` + > [!NOTE] + > If you specified a namespace in step 2, put this class in the same namespace. For example, add `namespace GeneratedCode;` below the `using` lines. + 7. Extend your demo code to send a partial PATCH request with the help of our package: ```c# @@ -72,11 +77,11 @@ The easiest way to get started is by using the built-in capabilities of Visual S }; // This line results in sending "lastName: null" instead of omitting it. - using (apiClient.RegisterAttributesForRequestDocument(patchRequest, person => person.LastName)) + using (apiClient.WithPartialAttributeSerialization(patchRequest, + person => person.LastName)) { PersonPrimaryResponseDocument patchResponse = - await apiClient.PatchPersonAsync("1", patchRequest); + await apiClient.PatchPersonAsync(1, patchRequest); // The sent request looks like this: // { @@ -100,19 +105,19 @@ Alternatively, the next section shows what to add to your client project file di ```xml - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + http://localhost:14140/swagger/v1/swagger.json diff --git a/docs/usage/openapi.md b/docs/usage/openapi.md index f0880e1c5f..fb37a60954 100644 --- a/docs/usage/openapi.md +++ b/docs/usage/openapi.md @@ -16,9 +16,10 @@ JsonApiDotNetCore provides an extension package that enables you to produce an [ ```c# IMvcCoreBuilder mvcCoreBuilder = builder.Services.AddMvcCore(); + // Include the mvcBuilder parameter. builder.Services.AddJsonApi(mvcBuilder: mvcCoreBuilder); - // Configures Swashbuckle for JSON:API. + // Configure Swashbuckle for JSON:API. builder.Services.AddOpenApi(mvcCoreBuilder); var app = builder.Build(); @@ -26,7 +27,7 @@ JsonApiDotNetCore provides an extension package that enables you to produce an [ app.UseRouting(); app.UseJsonApi(); - // Adds the Swashbuckle middleware. + // Add the Swashbuckle middleware. app.UseSwagger(); ``` @@ -34,7 +35,7 @@ By default, the OpenAPI specification will be available at `http://localhost: Date: Sun, 1 Oct 2023 22:38:23 +0200 Subject: [PATCH 56/66] Simplify code --- .../OpenApiEndpointConvention.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs index 09c5b8c650..fdae3e5d78 100644 --- a/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs +++ b/src/JsonApiDotNetCore.OpenApi/OpenApiEndpointConvention.cs @@ -79,11 +79,9 @@ private static bool IsSecondaryOrRelationshipEndpoint(JsonApiEndpoint endpoint) JsonApiEndpoint.PatchRelationship or JsonApiEndpoint.DeleteRelationship; } - private void SetResponseMetadata(ActionModel action, JsonApiEndpoint endpoint) + private static void SetResponseMetadata(ActionModel action, JsonApiEndpoint endpoint) { - IList statusCodes = GetStatusCodesForEndpoint(endpoint); - - foreach (int statusCode in statusCodes) + foreach (int statusCode in GetStatusCodesForEndpoint(endpoint)) { action.Filters.Add(new ProducesResponseTypeAttribute(statusCode)); @@ -103,7 +101,7 @@ private void SetResponseMetadata(ActionModel action, JsonApiEndpoint endpoint) } } - private static IList GetStatusCodesForEndpoint(JsonApiEndpoint endpoint) + private static IEnumerable GetStatusCodesForEndpoint(JsonApiEndpoint endpoint) { return endpoint switch { From c3c4844287917ec1fb5fb78256ddccb2ef182f9c Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:51:49 +0200 Subject: [PATCH 57/66] Remove redundant suppression --- test/TestBuildingBlocks/NullabilityAssertionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs b/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs index 0627449664..90d4eaba7c 100644 --- a/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs +++ b/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs @@ -2,7 +2,6 @@ using JetBrains.Annotations; using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; -// ReSharper disable PossibleMultipleEnumeration #pragma warning disable CS8777 // Parameter must have a non-null value when exiting. namespace TestBuildingBlocks; From f46db8a1c3841421d2fa7d5d2d98689154d83683 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 1 Oct 2023 23:18:05 +0200 Subject: [PATCH 58/66] Combine OpenAPI client tests for create resource with null/default attribute --- .../BaseOpenApiClientTests.cs | 4 +- .../CreateResourceTests.cs | 49 ++--------------- .../CreateResourceTests.cs | 49 ++--------------- .../CreateResourceTests.cs | 53 ++----------------- .../CreateResourceTests.cs | 49 ++--------------- .../JsonElementAssertionExtensions.cs | 32 +++++++---- 6 files changed, 42 insertions(+), 194 deletions(-) diff --git a/test/OpenApiClientTests/BaseOpenApiClientTests.cs b/test/OpenApiClientTests/BaseOpenApiClientTests.cs index a8ba84de9a..6ebfa04e18 100644 --- a/test/OpenApiClientTests/BaseOpenApiClientTests.cs +++ b/test/OpenApiClientTests/BaseOpenApiClientTests.cs @@ -23,7 +23,7 @@ public abstract class BaseOpenApiClientTests /// /// Sets the property on the specified source to its default value (null for string, 0 for int, false for bool, etc). /// - protected static void SetPropertyToDefaultValue(T source, string propertyName) + protected static object? SetPropertyToDefaultValue(T source, string propertyName) where T : class { ArgumentGuard.NotNull(source); @@ -33,6 +33,8 @@ protected static void SetPropertyToDefaultValue(T source, string propertyName object? defaultValue = property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; property.SetValue(source, defaultValue); + + return defaultValue; } /// diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 90f846742b..f05307e39c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -17,51 +17,10 @@ public sealed class CreateResourceTests : BaseOpenApiClientTests [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.attributes").With(attributesObject => - { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange @@ -80,7 +39,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); + object? defaultValue = SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); @@ -98,7 +57,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(defaultValue)); }); } diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 46c578c1e5..356a7f9cc3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -16,50 +16,9 @@ public sealed class CreateResourceTests : BaseOpenApiClientTests [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ReferenceType), "referenceType")] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.attributes").With(attributesObject => - { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { @@ -79,7 +38,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); + object? defaultValue = SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); @@ -97,13 +56,13 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(defaultValue)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredReferenceType), "requiredReferenceType")] - public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 1ab6bd3a80..3403c6a4f1 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -17,53 +17,10 @@ public sealed class CreateResourceTests : BaseOpenApiClientTests [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] - [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] - public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.attributes").With(attributesObject => - { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] + [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] public async Task Can_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange @@ -84,7 +41,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); + object? defaultValue = SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); @@ -102,14 +59,14 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(defaultValue)); }); } [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] - public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index c915e44635..48e2a0c461 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -17,49 +17,6 @@ public sealed class CreateResourceTests : BaseOpenApiClientTests [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.NullableReferenceType), "nullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.NullableValueType), "nullableValueType")] - public async Task Can_clear_attribute(string attributePropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - - Expression> includeAttributeSelector = - CreateAttributeSelectorFor(attributePropertyName); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.attributes").With(attributesObject => - { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.ValueKind.Should().Be(JsonValueKind.Null)); - }); - } - - [Theory] [InlineData(nameof(ResourceAttributesInPostRequest.ValueType), "valueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredValueType), "requiredValueType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableValueType), "requiredNullableValueType")] @@ -83,7 +40,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam } }; - SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); + object? defaultValue = SetPropertyToDefaultValue(requestDocument.Data.Attributes, attributePropertyName); using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); @@ -101,7 +58,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam document.Should().ContainPath("data.attributes").With(attributesObject => { - attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(0)); + attributesObject.Should().ContainPath(jsonPropertyName).With(attribute => attribute.Should().Be(defaultValue)); }); } @@ -109,7 +66,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam [InlineData(nameof(ResourceAttributesInPostRequest.NonNullableReferenceType), "nonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNonNullableReferenceType), "requiredNonNullableReferenceType")] [InlineData(nameof(ResourceAttributesInPostRequest.RequiredNullableReferenceType), "requiredNullableReferenceType")] - public async Task Cannot_clear_attribute(string attributePropertyName, string jsonPropertyName) + public async Task Cannot_set_attribute_to_default_value(string attributePropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs index e73a217d81..b541c46754 100644 --- a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs @@ -90,16 +90,30 @@ public void NotContainPath(string path) pathToken.Should().BeNull(); } - public void Be(string value) + public void Be(object? value) { - _subject.ValueKind.Should().Be(JsonValueKind.String); - _subject.GetString().Should().Be(value); - } - - public void Be(int value) - { - _subject.ValueKind.Should().Be(JsonValueKind.Number); - _subject.GetInt32().Should().Be(value); + if (value == null) + { + _subject.ValueKind.Should().Be(JsonValueKind.Null); + } + else if (value is bool boolValue) + { + _subject.ValueKind.Should().Be(boolValue ? JsonValueKind.True : JsonValueKind.False); + } + else if (value is int intValue) + { + _subject.ValueKind.Should().Be(JsonValueKind.Number); + _subject.GetInt32().Should().Be(intValue); + } + else if (value is string stringValue) + { + _subject.ValueKind.Should().Be(JsonValueKind.String); + _subject.GetString().Should().Be(stringValue); + } + else + { + throw new NotSupportedException($"Unknown object of type '{value.GetType()}'."); + } } public void ContainArrayElement(string value) From 63bf071d65553cc6bb9dd486c83d62335a7c8fa1 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 2 Oct 2023 01:46:24 +0200 Subject: [PATCH 59/66] Fixup OpenAPI example and docs --- docs/usage/openapi-client.md | 71 ++++++++++++------- .../ExampleApiClient.cs | 44 ++++++++++++ .../OpenAPIs/swagger.json | 14 ++++ .../JsonApiDotNetCoreExampleClient/Program.cs | 54 +++++++++----- test/OpenApiClientTests/ApiResponse.cs | 10 +-- 5 files changed, 143 insertions(+), 50 deletions(-) diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md index 280ae8ede3..7596300fb2 100644 --- a/docs/usage/openapi-client.md +++ b/docs/usage/openapi-client.md @@ -2,48 +2,56 @@ You can generate a JSON:API client in various programming languages from the [OpenAPI specification](https://swagger.io/specification/) file that JsonApiDotNetCore APIs provide. -For C# .NET clients generated using [NSwag](https://github.com/RicoSuter/NSwag), we provide an additional package that introduces support for partial PATCH/POST requests. The issue here is that a property on a generated C# class being `null` could mean "set the value to `null` in the request" or "this is `null` because I never touched it". +For C# .NET clients generated using [NSwag](https://github.com/RicoSuter/NSwag), we provide an additional package +that introduces support for partial PATCH/POST requests. The concern here is that a property on a generated C# class +being `null` could mean "set the value to `null` in the request" or "this is `null` because I never touched it". ## Getting started ### Visual Studio -The easiest way to get started is by using the built-in capabilities of Visual Studio. The next steps describe how to generate a JSON:API client library and use our package. +The easiest way to get started is by using the built-in capabilities of Visual Studio. +The next steps describe how to generate a JSON:API client library and use our package. 1. In **Solution Explorer**, right-click your client project, select **Add** > **Service Reference** and choose **OpenAPI**. 2. On the next page, specify the OpenAPI URL to your JSON:API server, for example: `http://localhost:14140/swagger/v1/swagger.json`. - Optionally provide a class name and namespace and click **Finish**. - Visual Studio now downloads your swagger.json and updates your project file. This results in a pre-build step that generates the client code. + Specify `ExampleApiClient` as class name, optionally provide a namespace and click **Finish**. + Visual Studio now downloads your swagger.json and updates your project file. + This adds a pre-build step that generates the client code. > [!TIP] - > To later re-download swagger.json and regenerate the client code, right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon. + > To later re-download swagger.json and regenerate the client code, + > right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon. -3. Although not strictly required, we recommend to run package update now, which fixes some issues and removes the `Stream` parameter from generated calls. +3. Although not strictly required, we recommend to run package update now, which fixes some issues. -4. Add some demo code that calls one of your JSON:API endpoints. For example: +4. Add code that calls one of your JSON:API endpoints. ```c# using var httpClient = new HttpClient(); var apiClient = new ExampleApiClient("http://localhost:14140", httpClient); - PersonCollectionResponseDocument getResponse = - await apiClient.GetPersonCollectionAsync(); + PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(); foreach (PersonDataInResponse person in getResponse.Data) { - Console.WriteLine($"Found user {person.Id} named " + - $"'{person.Attributes.FirstName} {person.Attributes.LastName}'."); + Console.WriteLine($"Found person {person.Id}: {person.Attributes.DisplayName}"); } ``` 5. Add our client package to your project: - ``` - dotnet add package JsonApiDotNetCore.OpenApi.Client - ``` + ``` + dotnet add package JsonApiDotNetCore.OpenApi.Client + ``` -6. Add the following glue code to connect our package with your generated code. The code below assumes you specified `ExampleApiClient` as the class name in step 2. +6. Add the following glue code to connect our package with your generated code. + + > [!NOTE] + > The class name must be the same as specified in step 2. + > If you also specified a namespace, put this class in the same namespace. + > For example, add `namespace GeneratedCode;` below the `using` lines. ```c# using JsonApiDotNetCore.OpenApi.Client; @@ -58,8 +66,8 @@ The easiest way to get started is by using the built-in capabilities of Visual S } ``` - > [!NOTE] - > If you specified a namespace in step 2, put this class in the same namespace. For example, add `namespace GeneratedCode;` below the `using` lines. + > [!TIP] + > The project at src/Examples/JsonApiDotNetCoreExampleClient contains an enhanced version that logs the HTTP requests and responses. 7. Extend your demo code to send a partial PATCH request with the help of our package: @@ -71,17 +79,16 @@ The easiest way to get started is by using the built-in capabilities of Visual S Id = "1", Attributes = new PersonAttributesInPatchRequest { - FirstName = "Jack" + LastName = "Doe" } } }; - // This line results in sending "lastName: null" instead of omitting it. + // This line results in sending "firstName: null" instead of omitting it. using (apiClient.WithPartialAttributeSerialization(patchRequest, - person => person.LastName)) + person => person.FirstName)) { - PersonPrimaryResponseDocument patchResponse = - await apiClient.PatchPersonAsync(1, patchRequest); + await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, patchRequest)); // The sent request looks like this: // { @@ -89,12 +96,26 @@ The easiest way to get started is by using the built-in capabilities of Visual S // "type": "people", // "id": "1", // "attributes": { - // "firstName": "Jack", - // "lastName": null + // "firstName": null, + // "lastName": "Doe" // } // } // } } + + static async Task TranslateAsync(Func> operation) + where TResponse : class + { + try + { + return await operation(); + } + catch (ApiException exception) when (exception.StatusCode == 204) + { + // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 + return null; + } + } ``` ### Other IDEs @@ -117,7 +138,7 @@ Alternatively, the next section shows what to add to your client project file di - + http://localhost:14140/swagger/v1/swagger.json diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs b/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs index fdde8f8fc4..d2c9289b04 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs +++ b/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs @@ -11,6 +11,50 @@ partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) { SetSerializerSettingsForJsonApi(settings); + // Optional: Makes the JSON easier to read when logged. settings.Formatting = Formatting.Indented; } + + // Optional: Log outgoing request to the console. + partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string url) + { + using var _ = new UsingConsoleColor(ConsoleColor.Green); + + Console.WriteLine($"--> {request}"); + string? requestBody = request.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); + + if (!string.IsNullOrEmpty(requestBody)) + { + Console.WriteLine(); + Console.WriteLine(requestBody); + } + } + + // Optional: Log incoming response to the console. + partial void ProcessResponse(HttpClient client, HttpResponseMessage response) + { + using var _ = new UsingConsoleColor(ConsoleColor.Cyan); + + Console.WriteLine($"<-- {response}"); + string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + if (!string.IsNullOrEmpty(responseBody)) + { + Console.WriteLine(); + Console.WriteLine(responseBody); + } + } + + private sealed class UsingConsoleColor : IDisposable + { + public UsingConsoleColor(ConsoleColor foregroundColor) + { + Console.ForegroundColor = foregroundColor; + } + + public void Dispose() + { + Console.ResetColor(); + } + } } diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json b/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json index 7b5139257e..797f5e7708 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json +++ b/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json @@ -2051,6 +2051,10 @@ "additionalProperties": false }, "personAttributesInResponse": { + "required": [ + "displayName", + "lastName" + ], "type": "object", "properties": { "firstName": { @@ -2341,6 +2345,9 @@ "additionalProperties": false }, "tagAttributesInResponse": { + "required": [ + "name" + ], "type": "object", "properties": { "name": { @@ -2715,6 +2722,10 @@ "additionalProperties": false }, "todoItemAttributesInResponse": { + "required": [ + "description", + "priority" + ], "type": "object", "properties": { "description": { @@ -2970,6 +2981,9 @@ "additionalProperties": false }, "todoItemRelationshipsInResponse": { + "required": [ + "owner" + ], "type": "object", "properties": { "owner": { diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs index 4ca5e5f58d..3c5d66ea2b 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs +++ b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs @@ -1,26 +1,46 @@ -namespace JsonApiDotNetCoreExampleClient; +using JsonApiDotNetCoreExampleClient; -internal static class Program -{ - private const string BaseUrl = "http://localhost:14140"; +using var httpClient = new HttpClient(); +var apiClient = new ExampleApiClient("http://localhost:14140", httpClient); - private static async Task Main() - { - using var httpClient = new HttpClient(); +PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(); - ExampleApiClient exampleApiClient = new(BaseUrl, httpClient); +foreach (PersonDataInResponse person in getResponse.Data) +{ + Console.WriteLine($"Found person {person.Id}: {person.Attributes.DisplayName}"); +} - try - { - const int nonExistingId = int.MaxValue; - await exampleApiClient.DeletePersonAsync(nonExistingId); - } - catch (ApiException exception) +var patchRequest = new PersonPatchRequestDocument +{ + Data = new PersonDataInPatchRequest + { + Id = "1", + Attributes = new PersonAttributesInPatchRequest { - Console.WriteLine(exception.Response); + LastName = "Doe" } + } +}; - Console.WriteLine("Press any key to close."); - Console.ReadKey(); +// This line results in sending "firstName: null" instead of omitting it. +using (apiClient.WithPartialAttributeSerialization(patchRequest, person => person.FirstName)) +{ + await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, patchRequest)); +} + +Console.WriteLine("Press any key to close."); +Console.ReadKey(); + +static async Task TranslateAsync(Func> operation) + where TResponse : class +{ + try + { + return await operation(); + } + catch (ApiException exception) when (exception.StatusCode == 204) + { + // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 + return null; } } diff --git a/test/OpenApiClientTests/ApiResponse.cs b/test/OpenApiClientTests/ApiResponse.cs index 7ea398c73a..10e9ecf6ca 100644 --- a/test/OpenApiClientTests/ApiResponse.cs +++ b/test/OpenApiClientTests/ApiResponse.cs @@ -10,21 +10,15 @@ internal static class ApiResponse public static async Task TranslateAsync(Func> operation) where TResponse : class { - // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 - ArgumentGuard.NotNull(operation); try { return await operation(); } - catch (ApiException exception) + catch (ApiException exception) when (exception.StatusCode == 204) { - if (exception.StatusCode != 204) - { - throw; - } - + // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 return null; } } From ce8045d3ab6337897f17cfbd4d1e9b74f7b05680 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 2 Oct 2023 01:51:27 +0200 Subject: [PATCH 60/66] Revert "Merge branch 'master' into openapi-required-and-nullable-properties" This reverts commit 66a2dc4389fe848817a444871df65a5a473a615b, reversing changes made to c3c4844287917ec1fb5fb78256ddccb2ef182f9c. --- .../Middleware/HeaderConstants.cs | 2 +- .../ModelState/ModelStateValidationTests.cs | 4 +-- .../NamingConventions/KebabCasingTests.cs | 4 +-- .../NamingConventions/PascalCasingTests.cs | 2 +- .../PaginationWithTotalCountTests.cs | 4 +-- .../SparseFieldSets/SparseFieldSetTests.cs | 30 +++++++++---------- .../ReadWrite/Creating/CreateResourceTests.cs | 2 +- .../ReadWrite/Fetching/FetchResourceTests.cs | 2 +- .../FieldChainPatternParseTests.cs | 2 +- .../Queries/QueryExpressionRewriterTests.cs | 12 ++++---- .../CreateSortExpressionFromLambdaTests.cs | 2 +- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/JsonApiDotNetCore/Middleware/HeaderConstants.cs b/src/JsonApiDotNetCore/Middleware/HeaderConstants.cs index d290ba80eb..43a5989d59 100644 --- a/src/JsonApiDotNetCore/Middleware/HeaderConstants.cs +++ b/src/JsonApiDotNetCore/Middleware/HeaderConstants.cs @@ -8,5 +8,5 @@ namespace JsonApiDotNetCore.Middleware; public static class HeaderConstants { public const string MediaType = "application/vnd.api+json"; - public const string AtomicOperationsMediaType = $"{MediaType}; ext=\"https://jsonapi.org/ext/atomic\""; + public const string AtomicOperationsMediaType = MediaType + "; ext=\"https://jsonapi.org/ext/atomic\""; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs index 9e104eef01..5a0738b0a9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/ModelStateValidationTests.cs @@ -125,7 +125,7 @@ public async Task Cannot_create_resource_with_invalid_attribute_value() ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Input validation failed."); - error.Detail.Should().Be(@"The field Name must match the regular expression '^[\w\s]+$'."); + error.Detail.Should().Be("The field Name must match the regular expression '^[\\w\\s]+$'."); error.Source.ShouldNotBeNull(); error.Source.Pointer.Should().Be("/data/attributes/directoryName"); } @@ -534,7 +534,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Input validation failed."); - error.Detail.Should().Be(@"The field Name must match the regular expression '^[\w\s]+$'."); + error.Detail.Should().Be("The field Name must match the regular expression '^[\\w\\s]+$'."); error.Source.ShouldNotBeNull(); error.Source.Pointer.Should().Be("/data/attributes/directoryName"); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingTests.cs index f7d407696e..d70f50de0e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/KebabCasingTests.cs @@ -74,8 +74,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - string route = - $"/public-api/swimming-pools/{pool.StringId}/water-slides?filter=greaterThan(length-in-meters,'1')&fields[water-slides]=length-in-meters"; + string route = $"/public-api/swimming-pools/{pool.StringId}/water-slides" + + "?filter=greaterThan(length-in-meters,'1')&fields[water-slides]=length-in-meters"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingTests.cs index 9f71d9de16..59d442fcba 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NamingConventions/PascalCasingTests.cs @@ -79,7 +79,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - string route = $"/PublicApi/SwimmingPools/{pool.StringId}/WaterSlides?filter=greaterThan(LengthInMeters,'1')&fields[WaterSlides]=LengthInMeters"; + string route = $"/PublicApi/SwimmingPools/{pool.StringId}/WaterSlides" + "?filter=greaterThan(LengthInMeters,'1')&fields[WaterSlides]=LengthInMeters"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs index d2163d95cd..ad6f8a1609 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs @@ -323,10 +323,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Links.ShouldNotBeNull(); responseDocument.Links.Self.Should().Be($"{HostPrefix}{route}"); - responseDocument.Links.First.Should().Be($"{basePath}?page%5Bsize%5D=1"); + responseDocument.Links.First.Should().Be(basePath + "?page%5Bsize%5D=1"); responseDocument.Links.Last.Should().BeNull(); responseDocument.Links.Prev.Should().Be(responseDocument.Links.First); - responseDocument.Links.Next.Should().Be($"{basePath}?page%5Bnumber%5D=3&page%5Bsize%5D=1"); + responseDocument.Links.Next.Should().Be(basePath + "?page%5Bnumber%5D=3&page%5Bsize%5D=1"); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index 2dacb19ea7..44148b19e7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -71,7 +71,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Caption.Should().Be(post.Caption); postCaptured.Url.Should().BeNull(); } @@ -106,7 +106,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(post.Caption)); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Caption.Should().Be(post.Caption); postCaptured.Url.Should().BeNull(); } @@ -149,7 +149,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Caption.Should().BeNull(); postCaptured.Url.Should().BeNull(); } @@ -193,7 +193,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; + var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().BeNull(); @@ -240,7 +240,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Url.Should().Be(post.Url); postCaptured.Caption.Should().BeNull(); } @@ -299,7 +299,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Id.Should().Be(post.Id); postCaptured.Caption.Should().Be(post.Caption); @@ -361,7 +361,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var accountCaptured = (WebAccount)store.Resources.Should().ContainSingle(resource => resource is WebAccount).Which; + var accountCaptured = (WebAccount)store.Resources.Should().ContainSingle(resource => resource is WebAccount).And.Subject.Single(); accountCaptured.Id.Should().Be(account.Id); accountCaptured.DisplayName.Should().Be(account.DisplayName); @@ -423,7 +423,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; + var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Owner.ShouldNotBeNull(); blogCaptured.Owner.DisplayName.Should().Be(blog.Owner.DisplayName); @@ -476,7 +476,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[0].Attributes.ShouldContainKey("color").With(value => value.Should().Be(post.Labels.Single().Color)); responseDocument.Included[0].Relationships.Should().BeNull(); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Id.Should().Be(post.Id); postCaptured.Caption.Should().Be(post.Caption); @@ -532,7 +532,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Included[1].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(blog.Owner.Posts[0].Caption)); responseDocument.Included[1].Relationships.Should().BeNull(); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; + var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().Be(blog.Title); blogCaptured.PlatformName.Should().BeNull(); @@ -620,7 +620,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; + var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).And.Subject.Single(); blogCaptured.Id.Should().Be(blog.Id); blogCaptured.Title.Should().Be(blog.Title); blogCaptured.PlatformName.Should().BeNull(); @@ -656,7 +656,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(post.Caption)); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Id.Should().Be(post.Id); postCaptured.Caption.Should().Be(post.Caption); postCaptured.Url.Should().BeNull(); @@ -691,7 +691,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.ManyValue[0].Attributes.Should().BeNull(); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Id.Should().Be(post.Id); postCaptured.Url.Should().BeNull(); } @@ -824,7 +824,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Data.SingleValue.Attributes.ShouldContainKey("showAdvertisements").With(value => value.Should().Be(blog.ShowAdvertisements)); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); - var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; + var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).And.Subject.Single(); blogCaptured.ShowAdvertisements.Should().Be(blog.ShowAdvertisements); blogCaptured.IsPublished.Should().Be(blog.IsPublished); blogCaptured.Title.Should().Be(blog.Title); @@ -869,7 +869,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => value.Links.Related.ShouldNotBeNull(); }); - var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; + var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).And.Subject.Single(); postCaptured.Id.Should().Be(post.Id); postCaptured.Caption.Should().Be(post.Caption); postCaptured.Url.Should().Be(post.Url); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index 5f1f894a9b..3354afd6bb 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -684,7 +684,7 @@ public async Task Cannot_create_resource_on_unknown_resource_type_in_url() } }; - const string route = $"/{Unknown.ResourceType}"; + const string route = "/" + Unknown.ResourceType; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAsync(route, requestBody); diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index 853e09d95c..487c6d72d5 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -62,7 +62,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_get_primary_resources_for_unknown_type() { // Arrange - const string route = $"/{Unknown.ResourceType}"; + const string route = "/" + Unknown.ResourceType; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecuteGetAsync(route); diff --git a/test/JsonApiDotNetCoreTests/UnitTests/FieldChains/FieldChainPatternParseTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/FieldChains/FieldChainPatternParseTests.cs index d7e5c22cc1..bdb5c6c681 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/FieldChains/FieldChainPatternParseTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/FieldChains/FieldChainPatternParseTests.cs @@ -49,7 +49,7 @@ public void ParseFails(string patternText, string errorMessage) Action action = () => FieldChainPattern.Parse(patternSource.Text); // Assert - PatternFormatException exception = action.Should().ThrowExactly().Which; + PatternFormatException exception = action.Should().Throw().Which; exception.Message.Should().Be(errorMessage); exception.Position.Should().Be(patternSource.Position); exception.Pattern.Should().Be(patternSource.Text); diff --git a/test/JsonApiDotNetCoreTests/UnitTests/Queries/QueryExpressionRewriterTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/Queries/QueryExpressionRewriterTests.cs index 3d2d8faa85..3e4a6b2276 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/Queries/QueryExpressionRewriterTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/Queries/QueryExpressionRewriterTests.cs @@ -53,7 +53,7 @@ public void VisitInclude(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); @@ -76,7 +76,7 @@ public void VisitSparseFieldSet(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); @@ -136,7 +136,7 @@ public void VisitFilter(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); @@ -160,7 +160,7 @@ public void VisitSort(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); @@ -183,7 +183,7 @@ public void VisitPagination(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); @@ -208,7 +208,7 @@ public void VisitParameterScope(string expressionText, string expectedTypes) // Assert List visitedTypeNames = rewriter.ExpressionsVisited.Select(queryExpression => queryExpression.GetType().Name).ToList(); - List expectedTypeNames = expectedTypes.Split(',').Select(type => $"{type}Expression").ToList(); + List expectedTypeNames = expectedTypes.Split(',').Select(type => type + "Expression").ToList(); visitedTypeNames.Should().ContainInOrder(expectedTypeNames); visitedTypeNames.Should().HaveCount(expectedTypeNames.Count); diff --git a/test/JsonApiDotNetCoreTests/UnitTests/ResourceDefinitions/CreateSortExpressionFromLambdaTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/ResourceDefinitions/CreateSortExpressionFromLambdaTests.cs index 8b919a87cc..3d178073c7 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/ResourceDefinitions/CreateSortExpressionFromLambdaTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/ResourceDefinitions/CreateSortExpressionFromLambdaTests.cs @@ -317,7 +317,7 @@ public void Cannot_convert_concatenation_operator() // Act Action action = () => resourceDefinition.GetSortExpressionFromLambda(new JsonApiResourceDefinition.PropertySortOrder { - (file => $"{file.Name}:{file.Content}", ListSortDirection.Ascending) + (file => file.Name + ":" + file.Content, ListSortDirection.Ascending) }); // Assert From fac04717892d621a25b571c77f4deceb694a7e95 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 2 Oct 2023 02:45:19 +0200 Subject: [PATCH 61/66] Workaround for running OpenAPI tests on Windows --- .gitattributes | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..b508c0d946 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# When running OpenAPI tests, these committed files are downloaded and written to disk (so we'll know when something changes). +# On Windows, these text files are auto-converted to crlf on fetch, while the written downloaded files use lf line endings. +# Therefore, running the tests on Windows creates local changes. Staging them auto-converts back to crlf, which undoes the changes. +# To avoid this annoyance, the next line opts out of the auto-conversion and forces to lf. +swagger.g.json text eol=lf From 91944879a69175d857b367793f21894f8f4b7301 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 2 Oct 2023 03:20:27 +0200 Subject: [PATCH 62/66] Address failing InspectCode --- src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs | 2 ++ src/Examples/JsonApiDotNetCoreExampleClient/Program.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs b/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs index d2c9289b04..8a5b5d3c46 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs +++ b/src/Examples/JsonApiDotNetCoreExampleClient/ExampleApiClient.cs @@ -2,6 +2,8 @@ using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; +// ReSharper disable UnusedParameterInPartialMethod + namespace JsonApiDotNetCoreExampleClient; [UsedImplicitly(ImplicitUseTargetFlags.Itself)] diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs index 3c5d66ea2b..1ef318fa65 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs +++ b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs @@ -31,6 +31,7 @@ Console.WriteLine("Press any key to close."); Console.ReadKey(); +// ReSharper disable once UnusedLocalFunctionReturnValue static async Task TranslateAsync(Func> operation) where TResponse : class { From 85d608bc758b047f41515994a5e3516204b79c38 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:02:52 +0200 Subject: [PATCH 63/66] Remove redundant calls --- .../ModelStateValidationOff/UpdateResourceTests.cs | 2 -- .../ModelStateValidationOn/UpdateResourceTests.cs | 2 -- .../ModelStateValidationOff/UpdateResourceTests.cs | 2 -- .../ModelStateValidationOn/UpdateResourceTests.cs | 2 -- 4 files changed, 8 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index 9e87362b11..baad3e94d6 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -118,8 +118,6 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index cad993df00..9d049df43b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -118,8 +118,6 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index 1fe393e225..f17e9e08d3 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -128,8 +128,6 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 99b95bf578..11876a7a02 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -128,8 +128,6 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); From 007ff3036f66994090b31658bcadb87e4cdf1ac0 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:09:26 +0200 Subject: [PATCH 64/66] Remove redundant tests --- .../CreateResourceTests.cs | 124 +----------- .../CreateResourceTests.cs | 164 +--------------- .../CreateResourceTests.cs | 174 +---------------- .../CreateResourceTests.cs | 176 +----------------- 4 files changed, 15 insertions(+), 623 deletions(-) diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index f05307e39c..684a961a1b 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -144,47 +144,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -222,7 +182,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -245,8 +205,6 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act Func> action = async () => await apiClient.PostResourceAsync(requestDocument); @@ -257,86 +215,10 @@ public async Task Cannot_clear_relationship_with_partial_attribute_serialization $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); } - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.Should().NotContainPath(jsonPropertyName); - }); - } - [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index 356a7f9cc3..f6eb4c4f1e 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -181,46 +181,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -259,46 +220,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -335,48 +257,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.Should().NotContainPath(jsonPropertyName); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToOne), "toOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -413,44 +294,7 @@ public async Task Can_omit_relationship_without_partial_attribute_serialization( [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - ToOne = _fakers.NullableToOne.Generate(), - RequiredToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToOne), "requiredToOne")] - public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index 3403c6a4f1..ec143cb9ee 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -195,49 +195,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -279,49 +237,7 @@ public async Task Can_clear_relationship_without_partial_attribute_serialization [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -360,7 +276,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -385,8 +301,6 @@ public async Task Can_omit_relationship_with_partial_attribute_serialization(str using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); @@ -399,89 +313,9 @@ public async Task Can_omit_relationship_with_partial_attribute_serialization(str }); } - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.Should().NotContainPath(jsonPropertyName); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.NullableToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } - [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index 48e2a0c461..b569c008ff 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -193,7 +193,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] - public async Task Can_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -218,8 +218,6 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - // Act await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); @@ -232,95 +230,13 @@ public async Task Can_clear_relationship_with_partial_attribute_serialization(st }); } - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] - public async Task Can_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath($"data.relationships.{jsonPropertyName}.data").With(relationshipDataObject => - { - relationshipDataObject.ValueKind.Should().Be(JsonValueKind.Null); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetDataPropertyToNull(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'data'. Property requires a value. Path 'data.relationships.{jsonPropertyName}'."); - } - [Theory] [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Cannot_clear_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_clear_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -359,50 +275,7 @@ public async Task Cannot_clear_relationship_without_partial_attribute_serializat [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); - - // Assert - JsonElement document = wrapper.GetRequestBodyAsJson(); - - document.Should().ContainPath("data.relationships").With(relationshipsObject => - { - relationshipsObject.Should().NotContainPath(jsonPropertyName); - }); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NullableToOne), "nullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.ToMany), "toMany")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredToMany), "requiredToMany")] - public async Task Can_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Can_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument @@ -443,48 +316,7 @@ public async Task Can_omit_relationship_without_partial_attribute_serialization( [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_omit_relationship_with_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) - { - // Arrange - var requestDocument = new ResourcePostRequestDocument - { - Data = new ResourceDataInPostRequest - { - Attributes = _fakers.PostAttributes.Generate(), - Relationships = new ResourceRelationshipsInPostRequest - { - NonNullableToOne = _fakers.ToOne.Generate(), - RequiredNonNullableToOne = _fakers.ToOne.Generate(), - NullableToOne = _fakers.NullableToOne.Generate(), - RequiredNullableToOne = _fakers.ToOne.Generate(), - ToMany = _fakers.ToMany.Generate(), - RequiredToMany = _fakers.ToMany.Generate() - } - } - }; - - SetPropertyToInitialValue(requestDocument.Data.Relationships, relationshipPropertyName); - - using var wrapper = FakeHttpClientWrapper.Create(HttpStatusCode.NoContent, null); - var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); - - using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); - - // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); - - // Assert - ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); - - assertion.Which.Message.Should().Be( - $"Cannot write a null value for property 'id'. Property requires a value. Path 'data.relationships.{jsonPropertyName}.data'."); - } - - [Theory] - [InlineData(nameof(ResourceRelationshipsInPostRequest.NonNullableToOne), "nonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNonNullableToOne), "requiredNonNullableToOne")] - [InlineData(nameof(ResourceRelationshipsInPostRequest.RequiredNullableToOne), "requiredNullableToOne")] - public async Task Cannot_omit_relationship_without_partial_attribute_serialization(string relationshipPropertyName, string jsonPropertyName) + public async Task Cannot_omit_relationship(string relationshipPropertyName, string jsonPropertyName) { // Arrange var requestDocument = new ResourcePostRequestDocument From 817aeb3ecbed6c02a3bf0e83d67f45518286ba65 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 3 Oct 2023 01:14:36 +0200 Subject: [PATCH 65/66] Move types out of the wrong namespace --- .../JsonApiMetadata/JsonApiEndpointMetadataProvider.cs | 1 - .../NonPrimaryDocumentTypeFactory.cs | 2 +- .../RelationshipTypeFactory.cs | 2 +- .../SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) rename src/JsonApiDotNetCore.OpenApi/{JsonApiObjects => JsonApiMetadata}/NonPrimaryDocumentTypeFactory.cs (98%) rename src/JsonApiDotNetCore.OpenApi/{JsonApiObjects => JsonApiMetadata}/RelationshipTypeFactory.cs (96%) diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs index 303fa959cc..ad8360591c 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs @@ -1,7 +1,6 @@ using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; using JsonApiDotNetCore.Resources.Annotations; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryDocumentTypeFactory.cs similarity index 98% rename from src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs rename to src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryDocumentTypeFactory.cs index 387c181f8b..a8f1ce1d8d 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NonPrimaryDocumentTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/NonPrimaryDocumentTypeFactory.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.Resources.Annotations; -namespace JsonApiDotNetCore.OpenApi.JsonApiObjects; +namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata; internal sealed class NonPrimaryDocumentTypeFactory { diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/RelationshipTypeFactory.cs similarity index 96% rename from src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs rename to src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/RelationshipTypeFactory.cs index 06efd7622f..033d875b31 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/RelationshipTypeFactory.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiMetadata/RelationshipTypeFactory.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.Resources.Annotations; -namespace JsonApiDotNetCore.OpenApi.JsonApiObjects; +namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata; internal sealed class RelationshipTypeFactory { diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 9b31d2c31f..054c49d2ea 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,5 +1,5 @@ using System.Text.Json; -using JsonApiDotNetCore.OpenApi.JsonApiObjects; +using JsonApiDotNetCore.OpenApi.JsonApiMetadata; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.Resources.Annotations; From 0dd49b0265fd6fcc9fabca171066112deae030fb Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:19:33 +0200 Subject: [PATCH 66/66] Remove redundant suppressions in openapi after update to CSharpGuidelinesAnalyzer v3.8.4 --- .../SwaggerComponents/CachingSwaggerGenerator.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs index f78ce52271..c4af79fc5d 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs @@ -3,8 +3,6 @@ using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; -#pragma warning disable AV1553 // Do not use optional parameters with default value null for strings, collections or tasks - namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; ///