diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ConsistencyGuard.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ConsistencyGuard.cs new file mode 100644 index 0000000000..a5ab4f69f5 --- /dev/null +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/ConsistencyGuard.cs @@ -0,0 +1,18 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace JsonApiDotNetCore.OpenApi.Swashbuckle; + +#pragma warning disable AV1008 // Class should not be static + +internal static class ConsistencyGuard +{ + [ExcludeFromCodeCoverage] + public static void ThrowIf([DoesNotReturnIf(true)] bool condition) + { + if (condition) + { + throw new UnreachableException(); + } + } +} diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs index d6a47f1ebc..6d63a540cd 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using JsonApiDotNetCore.Errors; using JsonApiDotNetCore.Middleware; @@ -115,18 +114,20 @@ private static List AddJsonApiMetadataToAction(ActionDescripto private static void UpdateProducesResponseTypeAttribute(ActionDescriptor endpoint, Type responseDocumentType) { + ProducesResponseTypeAttribute? attribute = null; + if (ProducesJsonApiResponseDocument(endpoint)) { var producesResponse = endpoint.GetFilterMetadata(); if (producesResponse != null) { - producesResponse.Type = responseDocumentType; - return; + attribute = producesResponse; } } - throw new UnreachableException(); + ConsistencyGuard.ThrowIf(attribute == null); + attribute.Type = responseDocumentType; } private static bool ProducesJsonApiResponseDocument(ActionDescriptor endpoint) diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs index b14f5e28ac..6fd6f9e42e 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiMetadata/JsonApiEndpointMetadataProvider.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; @@ -43,11 +42,7 @@ public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction) } ResourceType? primaryResourceType = _controllerResourceMapping.GetResourceTypeForController(controllerAction.ReflectedType); - - if (primaryResourceType == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(primaryResourceType == null); IJsonApiRequestMetadata? requestMetadata = GetRequestMetadata(endpoint, primaryResourceType); IJsonApiResponseMetadata? responseMetadata = GetResponseMetadata(endpoint, primaryResourceType); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiEndpointConvention.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiEndpointConvention.cs index 8136b2011c..75649b85a8 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiEndpointConvention.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiEndpointConvention.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Net; using System.Reflection; using JsonApiDotNetCore.Configuration; @@ -97,20 +96,56 @@ private static bool IsEndpointAvailable(JsonApiEndpoints endpoint, ResourceType // For an overridden JSON:API action method in a partial class to show up, it's flag must be turned on in [Resource]. // Otherwise, it is considered to be an action method that throws because the endpoint is unavailable. - return endpoint switch - { - JsonApiEndpoints.GetCollection => availableEndpoints.HasFlag(JsonApiEndpoints.GetCollection), - JsonApiEndpoints.GetSingle => availableEndpoints.HasFlag(JsonApiEndpoints.GetSingle), - JsonApiEndpoints.GetSecondary => availableEndpoints.HasFlag(JsonApiEndpoints.GetSecondary), - JsonApiEndpoints.GetRelationship => availableEndpoints.HasFlag(JsonApiEndpoints.GetRelationship), - JsonApiEndpoints.Post => availableEndpoints.HasFlag(JsonApiEndpoints.Post), - JsonApiEndpoints.PostRelationship => availableEndpoints.HasFlag(JsonApiEndpoints.PostRelationship), - JsonApiEndpoints.Patch => availableEndpoints.HasFlag(JsonApiEndpoints.Patch), - JsonApiEndpoints.PatchRelationship => availableEndpoints.HasFlag(JsonApiEndpoints.PatchRelationship), - JsonApiEndpoints.Delete => availableEndpoints.HasFlag(JsonApiEndpoints.Delete), - JsonApiEndpoints.DeleteRelationship => availableEndpoints.HasFlag(JsonApiEndpoints.DeleteRelationship), - _ => throw new UnreachableException() - }; + return IncludesEndpoint(endpoint, availableEndpoints); + } + + private static bool IncludesEndpoint(JsonApiEndpoints endpoint, JsonApiEndpoints availableEndpoints) + { + bool? isIncluded = null; + + if (endpoint == JsonApiEndpoints.GetCollection) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.GetCollection); + } + else if (endpoint == JsonApiEndpoints.GetSingle) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.GetSingle); + } + else if (endpoint == JsonApiEndpoints.GetSecondary) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.GetSecondary); + } + else if (endpoint == JsonApiEndpoints.GetRelationship) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.GetRelationship); + } + else if (endpoint == JsonApiEndpoints.Post) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.Post); + } + else if (endpoint == JsonApiEndpoints.PostRelationship) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.PostRelationship); + } + else if (endpoint == JsonApiEndpoints.Patch) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.Patch); + } + else if (endpoint == JsonApiEndpoints.PatchRelationship) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.PatchRelationship); + } + else if (endpoint == JsonApiEndpoints.Delete) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.Delete); + } + else if (endpoint == JsonApiEndpoints.DeleteRelationship) + { + isIncluded = availableEndpoints.HasFlag(JsonApiEndpoints.DeleteRelationship); + } + + ConsistencyGuard.ThrowIf(isIncluded == null); + return isIncluded.Value; } private static JsonApiEndpoints GetGeneratedControllerEndpoints(ResourceType resourceType) @@ -158,29 +193,40 @@ private static HttpStatusCode[] GetSuccessStatusCodesForEndpoint(JsonApiEndpoint ]; } - return endpoint.Value switch + HttpStatusCode[]? statusCodes = null; + + if (endpoint.Value is JsonApiEndpoints.GetCollection or JsonApiEndpoints.GetSingle or JsonApiEndpoints.GetSecondary or JsonApiEndpoints.GetRelationship) { - JsonApiEndpoints.GetCollection or JsonApiEndpoints.GetSingle or JsonApiEndpoints.GetSecondary or JsonApiEndpoints.GetRelationship => + statusCodes = [ HttpStatusCode.OK, HttpStatusCode.NotModified - ], - JsonApiEndpoints.Post => + ]; + } + else if (endpoint.Value == JsonApiEndpoints.Post) + { + statusCodes = [ HttpStatusCode.Created, HttpStatusCode.NoContent - ], - JsonApiEndpoints.Patch => + ]; + } + else if (endpoint.Value == JsonApiEndpoints.Patch) + { + statusCodes = [ HttpStatusCode.OK, HttpStatusCode.NoContent - ], - JsonApiEndpoints.Delete or JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or JsonApiEndpoints.DeleteRelationship => - [ - HttpStatusCode.NoContent - ], - _ => throw new UnreachableException() - }; + ]; + } + else if (endpoint.Value is JsonApiEndpoints.Delete or JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or + JsonApiEndpoints.DeleteRelationship) + { + statusCodes = [HttpStatusCode.NoContent]; + } + + ConsistencyGuard.ThrowIf(statusCodes == null); + return statusCodes; } private HttpStatusCode[] GetErrorStatusCodesForEndpoint(JsonApiEndpointWrapper endpoint, ResourceType? resourceType) @@ -200,46 +246,58 @@ private HttpStatusCode[] GetErrorStatusCodesForEndpoint(JsonApiEndpointWrapper e // Condition doesn't apply to atomic operations, because Forbidden is also used when an operation is not accessible. ClientIdGenerationMode clientIdGeneration = resourceType?.ClientIdGeneration ?? _options.ClientIdGeneration; - return endpoint.Value switch + HttpStatusCode[]? statusCodes = null; + + if (endpoint.Value == JsonApiEndpoints.GetCollection) + { + statusCodes = [HttpStatusCode.BadRequest]; + } + else if (endpoint.Value is JsonApiEndpoints.GetSingle or JsonApiEndpoints.GetSecondary or JsonApiEndpoints.GetRelationship) { - JsonApiEndpoints.GetCollection => [HttpStatusCode.BadRequest], - JsonApiEndpoints.GetSingle or JsonApiEndpoints.GetSecondary or JsonApiEndpoints.GetRelationship => + statusCodes = [ HttpStatusCode.BadRequest, HttpStatusCode.NotFound - ], - JsonApiEndpoints.Post when clientIdGeneration == ClientIdGenerationMode.Forbidden => + ]; + } + else if (endpoint.Value == JsonApiEndpoints.Post && clientIdGeneration == ClientIdGenerationMode.Forbidden) + { + statusCodes = [ HttpStatusCode.BadRequest, HttpStatusCode.Forbidden, HttpStatusCode.NotFound, HttpStatusCode.Conflict, HttpStatusCode.UnprocessableEntity - ], - JsonApiEndpoints.Post => - [ - HttpStatusCode.BadRequest, - HttpStatusCode.NotFound, - HttpStatusCode.Conflict, - HttpStatusCode.UnprocessableEntity - ], - JsonApiEndpoints.Patch => + ]; + } + else if (endpoint.Value is JsonApiEndpoints.Post or JsonApiEndpoints.Patch) + { + statusCodes = [ HttpStatusCode.BadRequest, HttpStatusCode.NotFound, HttpStatusCode.Conflict, HttpStatusCode.UnprocessableEntity - ], - JsonApiEndpoints.Delete => [HttpStatusCode.NotFound], - JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or JsonApiEndpoints.DeleteRelationship => + ]; + } + else if (endpoint.Value == JsonApiEndpoints.Delete) + { + statusCodes = [HttpStatusCode.NotFound]; + } + else if (endpoint.Value is JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or JsonApiEndpoints.DeleteRelationship) + { + statusCodes = [ HttpStatusCode.BadRequest, HttpStatusCode.NotFound, HttpStatusCode.Conflict, HttpStatusCode.UnprocessableEntity - ], - _ => throw new UnreachableException() - }; + ]; + } + + ConsistencyGuard.ThrowIf(statusCodes == null); + return statusCodes; } private void SetRequestMetadata(ActionModel action, JsonApiEndpointWrapper endpoint) diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiOperationIdSelector.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiOperationIdSelector.cs index 2688fce4e2..ed11481e27 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiOperationIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiOperationIdSelector.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using System.Text.Json; using Humanizer; @@ -64,23 +63,14 @@ public string GetOpenApiOperationId(ApiDescription endpoint) private static string GetTemplate(ApiDescription endpoint) { Type bodyType = GetBodyType(endpoint); - - if (!SchemaOpenTypeToOpenApiOperationIdTemplateMap.TryGetValue(bodyType, out string? template)) - { - throw new UnreachableException(); - } - + ConsistencyGuard.ThrowIf(!SchemaOpenTypeToOpenApiOperationIdTemplateMap.TryGetValue(bodyType, out string? template)); return template; } private static Type GetBodyType(ApiDescription endpoint) { var producesResponseTypeAttribute = endpoint.ActionDescriptor.GetFilterMetadata(); - - if (producesResponseTypeAttribute == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(producesResponseTypeAttribute == null); ControllerParameterDescriptor? requestBodyDescriptor = endpoint.ActionDescriptor.GetBodyParameterDescriptor(); Type bodyType = (requestBodyDescriptor?.ParameterType ?? producesResponseTypeAttribute.Type).ConstructedToOpenType(); @@ -95,10 +85,8 @@ private static Type GetBodyType(ApiDescription endpoint) private string ApplyTemplate(string openApiOperationIdTemplate, ResourceType? resourceType, ApiDescription endpoint) { - if (endpoint.RelativePath == null || endpoint.HttpMethod == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(endpoint.RelativePath == null); + ConsistencyGuard.ThrowIf(endpoint.HttpMethod == null); string method = endpoint.HttpMethod.ToLowerInvariant(); string relationshipName = openApiOperationIdTemplate.Contains("[RelationshipName]") ? endpoint.RelativePath.Split('/').Last() : string.Empty; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs index aa0e24e39a..10095834b2 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; @@ -20,10 +19,7 @@ public static void ReorderProperties(this OpenApiSchema fullSchema, IEnumerable< } } - if (fullSchema.Properties.Count != propertiesInOrder.Count) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(fullSchema.Properties.Count != propertiesInOrder.Count); fullSchema.Properties = propertiesInOrder; } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs index 02c831b212..366aa1055d 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; @@ -64,11 +63,7 @@ public OpenApiSchema GenerateSchema(Type dataContainerSchemaType, ResourceType r private static Type GetElementTypeOfDataProperty(Type dataContainerConstructedType, ResourceType resourceType) { PropertyInfo? dataProperty = dataContainerConstructedType.GetProperty("Data"); - - if (dataProperty == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(dataProperty == null); Type innerPropertyType = dataProperty.PropertyType.ConstructedToOpenType().IsAssignableTo(typeof(ICollection<>)) ? dataProperty.PropertyType.GenericTypeArguments[0] @@ -79,10 +74,7 @@ private static Type GetElementTypeOfDataProperty(Type dataContainerConstructedTy return typeof(DataInResponse<>).MakeGenericType(resourceType.ClrType); } - if (!innerPropertyType.IsGenericType) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(!innerPropertyType.IsGenericType); return innerPropertyType; } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs index c2fbefa09c..66cb83a5c4 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs @@ -1,6 +1,6 @@ using System.Collections.Concurrent; -using System.Diagnostics; using System.Reflection; +using System.Runtime.CompilerServices; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; @@ -144,62 +144,55 @@ public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, private static Type? GetCommonSchemaType(Type schemaOpenType) { + StrongBox? boxedSchemaType = null; + if (schemaOpenType == typeof(IdentifierInRequest<>)) { - return typeof(IdentifierInRequest); + boxedSchemaType = new StrongBox(typeof(IdentifierInRequest)); } - - if (schemaOpenType == typeof(DataInCreateRequest<>)) + else if (schemaOpenType == typeof(DataInCreateRequest<>)) { - return typeof(ResourceInCreateRequest); + boxedSchemaType = new StrongBox(typeof(ResourceInCreateRequest)); } - - if (schemaOpenType == typeof(AttributesInCreateRequest<>)) + else if (schemaOpenType == typeof(AttributesInCreateRequest<>)) { - return typeof(AttributesInCreateRequest); + boxedSchemaType = new StrongBox(typeof(AttributesInCreateRequest)); } - - if (schemaOpenType == typeof(RelationshipsInCreateRequest<>)) + else if (schemaOpenType == typeof(RelationshipsInCreateRequest<>)) { - return typeof(RelationshipsInCreateRequest); + boxedSchemaType = new StrongBox(typeof(RelationshipsInCreateRequest)); } - - if (schemaOpenType == typeof(DataInUpdateRequest<>)) + else if (schemaOpenType == typeof(DataInUpdateRequest<>)) { - return typeof(ResourceInUpdateRequest); + boxedSchemaType = new StrongBox(typeof(ResourceInUpdateRequest)); } - - if (schemaOpenType == typeof(AttributesInUpdateRequest<>)) + else if (schemaOpenType == typeof(AttributesInUpdateRequest<>)) { - return typeof(AttributesInUpdateRequest); + boxedSchemaType = new StrongBox(typeof(AttributesInUpdateRequest)); } - - if (schemaOpenType == typeof(RelationshipsInUpdateRequest<>)) + else if (schemaOpenType == typeof(RelationshipsInUpdateRequest<>)) { - return typeof(RelationshipsInUpdateRequest); + boxedSchemaType = new StrongBox(typeof(RelationshipsInUpdateRequest)); } - - if (schemaOpenType == typeof(IdentifierInResponse<>)) + else if (schemaOpenType == typeof(IdentifierInResponse<>)) { - return null; + boxedSchemaType = new StrongBox(null); } - - if (schemaOpenType == typeof(DataInResponse<>)) + else if (schemaOpenType == typeof(DataInResponse<>)) { - return typeof(ResourceInResponse); + boxedSchemaType = new StrongBox(typeof(ResourceInResponse)); } - - if (schemaOpenType == typeof(AttributesInResponse<>)) + else if (schemaOpenType == typeof(AttributesInResponse<>)) { - return typeof(AttributesInResponse); + boxedSchemaType = new StrongBox(typeof(AttributesInResponse)); } - - if (schemaOpenType == typeof(RelationshipsInResponse<>)) + else if (schemaOpenType == typeof(RelationshipsInResponse<>)) { - return typeof(RelationshipsInResponse); + boxedSchemaType = new StrongBox(typeof(RelationshipsInResponse)); } - throw new UnreachableException(); + ConsistencyGuard.ThrowIf(boxedSchemaType == null); + return boxedSchemaType.Value; } public OpenApiSchema GenerateSchemaForCommonData(Type commonDataSchemaType, SchemaRepository schemaRepository) @@ -390,11 +383,7 @@ private void SetFieldSchemaMembers(OpenApiSchema fullSchemaForData, ResourceSche GetResourceSchemaTypeForFieldsProperty(resourceSchemaTypeForData, forAttributes ? "Attributes" : "Relationships"); Type? commonFieldsSchemaType = GetCommonSchemaType(resourceSchemaTypeForFields.SchemaOpenType); - - if (commonFieldsSchemaType == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(commonFieldsSchemaType == null); _ = GenerateSchemaForCommonFields(commonFieldsSchemaType, schemaRepository); @@ -432,11 +421,7 @@ private void SetFieldSchemaMembers(OpenApiSchema fullSchemaForData, ResourceSche private ResourceSchemaType GetResourceSchemaTypeForFieldsProperty(ResourceSchemaType resourceSchemaTypeForData, string propertyName) { PropertyInfo? fieldsProperty = resourceSchemaTypeForData.SchemaConstructedType.GetProperty(propertyName); - - if (fieldsProperty == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(fieldsProperty == null); Type fieldsConstructedType = fieldsProperty.PropertyType; return ResourceSchemaType.Create(fieldsConstructedType, _resourceGraph); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs index 5f9fecf402..c4f0ebf556 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.Resources.Annotations; @@ -52,11 +51,7 @@ public OpenApiSchema GenerateSchema(RelationshipAttribute relationship, SchemaRe } Type relationshipIdentifierConstructedType = typeof(RelationshipIdentifier<>).MakeGenericType(relationship.LeftType.ClrType); - - if (schemaRepository.TryLookupByType(relationshipIdentifierConstructedType, out _)) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(schemaRepository.TryLookupByType(relationshipIdentifierConstructedType, out _)); OpenApiSchema referenceSchemaForIdentifier = _defaultSchemaGenerator.GenerateSchema(relationshipIdentifierConstructedType, schemaRepository); OpenApiSchema fullSchemaForIdentifier = schemaRepository.Schemas[referenceSchemaForIdentifier.Reference.Id]; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs index d3a4ec50de..49a0002531 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using JsonApiDotNetCore.AtomicOperations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; @@ -158,13 +157,7 @@ private void GenerateSchemaForOperation(ResourceType resourceType, SchemaReposit private void GenerateSchemaForResourceOperation(Type operationOpenType, ResourceType resourceType, AtomicOperationCode operationCode, SchemaRepository schemaRepository) { - WriteOperationKind writeOperation = operationCode switch - { - AtomicOperationCode.Add => WriteOperationKind.CreateResource, - AtomicOperationCode.Update => WriteOperationKind.UpdateResource, - AtomicOperationCode.Remove => WriteOperationKind.DeleteResource, - _ => throw new UnreachableException() - }; + WriteOperationKind writeOperation = GetKindOfResourceOperation(operationCode); if (IsResourceTypeEnabled(resourceType, writeOperation)) { @@ -212,6 +205,27 @@ private void GenerateSchemaForResourceOperation(Type operationOpenType, Resource } } + private static WriteOperationKind GetKindOfResourceOperation(AtomicOperationCode operationCode) + { + WriteOperationKind? writeOperation = null; + + if (operationCode == AtomicOperationCode.Add) + { + writeOperation = WriteOperationKind.CreateResource; + } + else if (operationCode == AtomicOperationCode.Update) + { + writeOperation = WriteOperationKind.UpdateResource; + } + else if (operationCode == AtomicOperationCode.Remove) + { + writeOperation = WriteOperationKind.DeleteResource; + } + + ConsistencyGuard.ThrowIf(writeOperation == null); + return writeOperation.Value; + } + private bool IsResourceTypeEnabled(ResourceType resourceType, WriteOperationKind writeOperation) { return _atomicOperationFilter.IsEnabled(resourceType, writeOperation); @@ -277,13 +291,7 @@ private static void IncludeRelationshipsInDerivedType(ResourceType derivedType, private void GenerateSchemaForRelationshipOperation(Type operationOpenType, RelationshipAttribute relationship, AtomicOperationCode operationCode, SchemaRepository schemaRepository) { - WriteOperationKind writeOperation = operationCode switch - { - AtomicOperationCode.Add => WriteOperationKind.AddToRelationship, - AtomicOperationCode.Update => WriteOperationKind.SetRelationship, - AtomicOperationCode.Remove => WriteOperationKind.RemoveFromRelationship, - _ => throw new UnreachableException() - }; + WriteOperationKind writeOperation = GetKindOfRelationshipOperation(operationCode); if (!IsRelationshipEnabled(relationship, writeOperation)) { @@ -336,11 +344,7 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela RemoveProperties(inlineSchemaForOperation); string baseRelationshipSchemaId = _schemaIdSelector.GetRelationshipAtomicOperationSchemaId(relationshipInAnyBaseResourceType, operationCode); - - if (!schemaRepository.Schemas.ContainsKey(baseRelationshipSchemaId)) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(!schemaRepository.Schemas.ContainsKey(baseRelationshipSchemaId)); fullSchemaForOperation.AllOf[0] = new OpenApiSchema { @@ -356,6 +360,27 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela MapInDiscriminator(referenceSchemaForOperation, discriminatorValue, schemaRepository); } + private static WriteOperationKind GetKindOfRelationshipOperation(AtomicOperationCode operationCode) + { + WriteOperationKind? writeOperation = null; + + if (operationCode == AtomicOperationCode.Add) + { + writeOperation = WriteOperationKind.AddToRelationship; + } + else if (operationCode == AtomicOperationCode.Update) + { + writeOperation = WriteOperationKind.SetRelationship; + } + else if (operationCode == AtomicOperationCode.Remove) + { + writeOperation = WriteOperationKind.RemoveFromRelationship; + } + + ConsistencyGuard.ThrowIf(writeOperation == null); + return writeOperation.Value; + } + private bool IsRelationshipEnabled(RelationshipAttribute relationship, WriteOperationKind writeOperation) { if (!_atomicOperationFilter.IsEnabled(relationship.LeftType, writeOperation)) @@ -378,22 +403,36 @@ private bool IsRelationshipEnabled(RelationshipAttribute relationship, WriteOper private static bool IsToOneRelationshipEnabled(HasOneAttribute relationship, WriteOperationKind writeOperation) { - return writeOperation switch + bool? isEnabled = null; + + if (writeOperation == WriteOperationKind.SetRelationship) { - WriteOperationKind.SetRelationship => relationship.Capabilities.HasFlag(HasOneCapabilities.AllowSet), - _ => throw new UnreachableException() - }; + isEnabled = relationship.Capabilities.HasFlag(HasOneCapabilities.AllowSet); + } + + ConsistencyGuard.ThrowIf(isEnabled == null); + return isEnabled.Value; } private static bool IsToManyRelationshipEnabled(HasManyAttribute relationship, WriteOperationKind writeOperation) { - return writeOperation switch + bool? isEnabled = null; + + if (writeOperation == WriteOperationKind.SetRelationship) { - WriteOperationKind.SetRelationship => relationship.Capabilities.HasFlag(HasManyCapabilities.AllowSet), - WriteOperationKind.AddToRelationship => relationship.Capabilities.HasFlag(HasManyCapabilities.AllowAdd), - WriteOperationKind.RemoveFromRelationship => relationship.Capabilities.HasFlag(HasManyCapabilities.AllowRemove), - _ => throw new UnreachableException() - }; + isEnabled = relationship.Capabilities.HasFlag(HasManyCapabilities.AllowSet); + } + else if (writeOperation == WriteOperationKind.AddToRelationship) + { + isEnabled = relationship.Capabilities.HasFlag(HasManyCapabilities.AllowAdd); + } + else if (writeOperation == WriteOperationKind.RemoveFromRelationship) + { + isEnabled = relationship.Capabilities.HasFlag(HasManyCapabilities.AllowRemove); + } + + ConsistencyGuard.ThrowIf(isEnabled == null); + return isEnabled.Value; } private RelationshipAttribute? GetRelationshipEnabledInAnyBase(RelationshipAttribute relationship, WriteOperationKind writeOperation) diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs index c05ab19c95..19d94eb48e 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -54,14 +53,18 @@ private static bool IsJsonApiParameter(ParameterInfo parameter) private DocumentSchemaGenerator GetDocumentSchemaGenerator(Type schemaType) { + DocumentSchemaGenerator? generator = null; + foreach (DocumentSchemaGenerator documentSchemaGenerator in _documentSchemaGenerators) { if (documentSchemaGenerator.CanGenerate(schemaType)) { - return documentSchemaGenerator; + generator = documentSchemaGenerator; + break; } } - throw new UnreachableException(); + ConsistencyGuard.ThrowIf(generator == null); + return generator; } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs index 187fd92f7a..1b5c0d5f4c 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Net; using System.Reflection; using Humanizer; @@ -439,10 +438,7 @@ private void ApplyDeleteRelationship(OpenApiOperation operation, RelationshipAtt private static RelationshipAttribute GetRelationshipFromRoute(ApiDescription apiDescription, ResourceType resourceType) { - if (apiDescription.RelativePath == null) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(apiDescription.RelativePath == null); string relationshipName = apiDescription.RelativePath.Split('/').Last(); return resourceType.GetRelationshipByPublicName(relationshipName); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs index d734828760..d7dcb25939 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; @@ -108,9 +107,23 @@ public void SetMembersOfAttributes(OpenApiSchema fullSchemaForAttributes, bool f private static AttrCapabilities GetRequiredCapabilityForAttributes(Type resourceDataOpenType) { - return resourceDataOpenType == typeof(DataInResponse<>) ? AttrCapabilities.AllowView : - resourceDataOpenType == typeof(DataInCreateRequest<>) ? AttrCapabilities.AllowCreate : - resourceDataOpenType == typeof(DataInUpdateRequest<>) ? AttrCapabilities.AllowChange : throw new UnreachableException(); + AttrCapabilities? capabilities = null; + + if (resourceDataOpenType == typeof(DataInResponse<>)) + { + capabilities = AttrCapabilities.AllowView; + } + else if (resourceDataOpenType == typeof(DataInCreateRequest<>)) + { + capabilities = AttrCapabilities.AllowCreate; + } + else if (resourceDataOpenType == typeof(DataInUpdateRequest<>)) + { + capabilities = AttrCapabilities.AllowChange; + } + + ConsistencyGuard.ThrowIf(capabilities == null); + return capabilities.Value; } private void EnsureAttributeSchemaIsExposed(OpenApiSchema referenceSchemaForAttribute, AttrAttribute attribute, SchemaRepository schemaRepository) @@ -221,9 +234,6 @@ private OpenApiSchema CreateReferenceSchemaForRelationship(Type relationshipSche private static void AssertHasNoProperties(OpenApiSchema fullSchema) { - if (fullSchema.Properties.Count > 0) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(fullSchema.Properties.Count > 0); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs index 2342d4feec..dec10db97b 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using JetBrains.Annotations; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; @@ -47,11 +46,7 @@ private static bool HasSortAnnotation(OpenApiSchema schema) private static void OrderEnumMembers(OpenApiSchema schema) { List ordered = schema.Enum.OfType().OrderBy(openApiString => openApiString.Value).Cast().ToList(); - - if (ordered.Count != schema.Enum.Count) - { - throw new UnreachableException(); - } + ConsistencyGuard.ThrowIf(ordered.Count != schema.Enum.Count); schema.Enum = ordered; }