From 919a3f8161424b75bcaaabc2e94aedefe804f7f9 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Tue, 20 Dec 2022 13:41:10 +0100 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] - 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 7/7] 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"); - }); - }); - } -} - -