diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index 6ae5a3a918..b5f9e22340 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -15,6 +15,8 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$); 50 False True + swagger.g.json + swagger.json SOLUTION True True diff --git a/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs b/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs new file mode 100644 index 0000000000..1224308af9 --- /dev/null +++ b/src/JsonApiDotNetCore.OpenApi.Client/ApiException.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; + +// We cannot rely on generating ApiException as soon as we are generating multiple clients, see https://github.com/RicoSuter/NSwag/issues/2839#issuecomment-776647377. +// Instead, we configure NSwag to point to the exception below in the generated code. + +// ReSharper disable once CheckNamespace +namespace JsonApiDotNetCore.OpenApi.Client.Exceptions; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ApiException : Exception +{ + public int StatusCode { get; } + + public string? Response { get; } + + public IReadOnlyDictionary> Headers { get; } + + public ApiException(string message, int statusCode, string? response, IReadOnlyDictionary> headers, Exception innerException) + : base($"{message}\n\nStatus: {statusCode}\nResponse: \n{response ?? "(null)"}", innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } +} diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableResourceIdentifierResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableResourceIdentifierResponseDocument.cs index 2eb2bff44d..ca04d0f853 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableResourceIdentifierResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableResourceIdentifierResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -6,14 +7,19 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; +// Types in the current namespace are never touched by ASP.NET ModelState validation, therefore using a non-nullable reference type for a property does not +// imply this property is required. Instead, we use [Required] explicitly, because this is how Swashbuckle is instructed to mark properties as required. [UsedImplicitly(ImplicitUseTargetFlags.Members)] internal sealed class NullableResourceIdentifierResponseDocument : NullableSingleData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceIdentifierDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableSecondaryResourceResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableSecondaryResourceResponseDocument.cs index 383e8301e9..93334854e0 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableSecondaryResourceResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableSecondaryResourceResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -10,10 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; internal sealed class NullableSecondaryResourceResponseDocument : NullableSingleData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/PrimaryResourceResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/PrimaryResourceResponseDocument.cs index f708972a94..fd34d149d9 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/PrimaryResourceResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/PrimaryResourceResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -6,16 +7,17 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; -// Types in the current namespace are never touched by ASP.NET ModelState validation, therefore using a non-nullable reference type for a property does not -// imply this property is required. Instead, we use [Required] explicitly, because this is how Swashbuckle is instructed to mark properties as required. [UsedImplicitly(ImplicitUseTargetFlags.Members)] internal sealed class PrimaryResourceResponseDocument : SingleData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceCollectionResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceCollectionResponseDocument.cs index 92bbc88803..7547718523 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceCollectionResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceCollectionResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -10,10 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; internal sealed class ResourceCollectionResponseDocument : ManyData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceCollectionDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierCollectionResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierCollectionResponseDocument.cs index 1efa833988..f2b0f3a885 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierCollectionResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierCollectionResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -10,10 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; internal sealed class ResourceIdentifierCollectionResponseDocument : ManyData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceIdentifierCollectionDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierResponseDocument.cs index bf87717555..1c99cbd517 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -10,10 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; internal sealed class ResourceIdentifierResponseDocument : SingleData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceIdentifierDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/SecondaryResourceResponseDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/SecondaryResourceResponseDocument.cs index 1cb601c87d..c048b34dfb 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/SecondaryResourceResponseDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/SecondaryResourceResponseDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -10,10 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; internal sealed class SecondaryResourceResponseDocument : SingleData> where TResource : IIdentifiable { + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; + [JsonPropertyName("jsonapi")] public JsonapiObject Jsonapi { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceDocument Links { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/JsonapiObject.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/JsonapiObject.cs index 1c7b497cb2..8ad3074e4f 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/JsonapiObject.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/JsonapiObject.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects; @@ -5,11 +6,15 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects; [UsedImplicitly(ImplicitUseTargetFlags.Members)] internal sealed class JsonapiObject { + [JsonPropertyName("version")] public string Version { get; set; } = null!; + [JsonPropertyName("ext")] public ICollection Ext { get; set; } = null!; + [JsonPropertyName("profile")] public ICollection Profile { get; set; } = null!; + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInRelationshipObject.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInRelationshipObject.cs index db689900b3..16b9735dac 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInRelationshipObject.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInRelationshipObject.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,8 +8,10 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInRelationshipObject { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; [Required] + [JsonPropertyName("related")] public string Related { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs index e2a96ef8e4..9ce49f9f58 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,16 +8,22 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInResourceCollectionDocument { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; + [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; [Required] + [JsonPropertyName("first")] public string First { get; set; } = null!; + [JsonPropertyName("last")] public string Last { get; set; } = null!; + [JsonPropertyName("prev")] public string Prev { get; set; } = null!; + [JsonPropertyName("next")] public string Next { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceDocument.cs index e22ce258bc..b83e59fa4d 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,7 +8,9 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInResourceDocument { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; + [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs index f440b118fd..a13a6175b9 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,19 +8,26 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInResourceIdentifierCollectionDocument { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; + [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; [Required] + [JsonPropertyName("related")] public string Related { get; set; } = null!; [Required] + [JsonPropertyName("first")] public string First { get; set; } = null!; + [JsonPropertyName("last")] public string Last { get; set; } = null!; + [JsonPropertyName("prev")] public string Prev { get; set; } = null!; + [JsonPropertyName("next")] public string Next { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierDocument.cs index 1fcca155cf..9304162d0f 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierDocument.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,10 +8,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInResourceIdentifierDocument { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; + [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; [Required] + [JsonPropertyName("related")] public string Related { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceObject.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceObject.cs index f8f7de61f2..552cc703e3 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceObject.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceObject.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; @@ -7,5 +8,6 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; internal sealed class LinksInResourceObject { [Required] + [JsonPropertyName("self")] public string Self { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ManyData.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ManyData.cs index 6d345efe5f..5f80fd0f2b 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ManyData.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ManyData.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -9,5 +10,6 @@ internal abstract class ManyData where TData : ResourceIdentifierObject { [Required] + [JsonPropertyName("data")] public ICollection Data { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NullableSingleData.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NullableSingleData.cs index 600bdf43b9..27063648cb 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NullableSingleData.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/NullableSingleData.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -9,5 +10,6 @@ internal abstract class NullableSingleData where TData : ResourceIdentifierObject { [Required] + [JsonPropertyName("data")] public TData? Data { get; set; } } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/NullableToOneRelationshipInResponse.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/NullableToOneRelationshipInResponse.cs index ed0cbd22fd..fdc45b811a 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/NullableToOneRelationshipInResponse.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/NullableToOneRelationshipInResponse.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -11,7 +12,9 @@ internal sealed class NullableToOneRelationshipInResponse : NullableS where TResource : IIdentifiable { [Required] + [JsonPropertyName("links")] public LinksInRelationshipObject Links { get; set; } = null!; + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToManyRelationshipInResponse.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToManyRelationshipInResponse.cs index 381e484f0b..14380f025e 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToManyRelationshipInResponse.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToManyRelationshipInResponse.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -11,7 +12,9 @@ internal sealed class ToManyRelationshipInResponse : ManyData Meta { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToOneRelationshipInResponse.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToOneRelationshipInResponse.cs index 962091a469..a867dffdc0 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToOneRelationshipInResponse.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Relationships/ToOneRelationshipInResponse.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -11,7 +12,9 @@ internal sealed class ToOneRelationshipInResponse : SingleData Meta { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceIdentifierObject.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceIdentifierObject.cs index 5024c3ed44..783a94d647 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceIdentifierObject.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceIdentifierObject.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Resources; @@ -14,8 +15,10 @@ internal sealed class ResourceIdentifierObject : ResourceIdentifierOb internal class ResourceIdentifierObject { [Required] + [JsonPropertyName("type")] public string Type { get; set; } = null!; [Required] + [JsonPropertyName("id")] public string Id { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPatchRequest.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPatchRequest.cs index fadb20969e..7263ededd4 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPatchRequest.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPatchRequest.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Resources; @@ -7,7 +8,9 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; internal sealed class ResourceObjectInPatchRequest : ResourceIdentifierObject where TResource : IIdentifiable { + [JsonPropertyName("attributes")] public AttributesInPatchRequest Attributes { get; set; } = null!; + [JsonPropertyName("relationships")] public RelationshipsInPatchRequest Relationships { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPostRequest.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPostRequest.cs index 71ad0f34c9..f886883b4e 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPostRequest.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPostRequest.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.Resources; @@ -7,7 +8,9 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; internal sealed class ResourceObjectInPostRequest : ResourceIdentifierObject where TResource : IIdentifiable { + [JsonPropertyName("attributes")] public AttributesInPostRequest Attributes { get; set; } = null!; + [JsonPropertyName("relationships")] public RelationshipsInPostRequest Relationships { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs index 30bf3d0ff6..ae61d9822c 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Links; using JsonApiDotNetCore.Resources; @@ -9,12 +10,16 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; internal sealed class ResourceObjectInResponse : ResourceIdentifierObject where TResource : IIdentifiable { + [JsonPropertyName("attributes")] public AttributesInResponse Attributes { get; set; } = null!; + [JsonPropertyName("relationships")] public RelationshipsInResponse Relationships { get; set; } = null!; [Required] + [JsonPropertyName("links")] public LinksInResourceObject Links { get; set; } = null!; + [JsonPropertyName("meta")] public IDictionary Meta { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/SingleData.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/SingleData.cs index 9ba0cd4416..019fc58c2d 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/SingleData.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/SingleData.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -9,5 +10,6 @@ internal abstract class SingleData where TData : ResourceIdentifierObject { [Required] + [JsonPropertyName("data")] public TData Data { get; set; } = null!; } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs index 7e3e2572a5..9724effbd5 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs @@ -36,7 +36,6 @@ internal sealed class JsonApiOperationIdSelector private readonly IControllerResourceMapping _controllerResourceMapping; private readonly JsonNamingPolicy? _namingPolicy; - private readonly ResourceNameFormatter _formatter; public JsonApiOperationIdSelector(IControllerResourceMapping controllerResourceMapping, JsonNamingPolicy? namingPolicy) { @@ -44,7 +43,6 @@ public JsonApiOperationIdSelector(IControllerResourceMapping controllerResourceM _controllerResourceMapping = controllerResourceMapping; _namingPolicy = namingPolicy; - _formatter = new ResourceNameFormatter(namingPolicy); } public string GetOperationId(ApiDescription endpoint) @@ -60,7 +58,7 @@ public string GetOperationId(ApiDescription endpoint) string template = GetTemplate(primaryResourceType.ClrType, endpoint); - return ApplyTemplate(template, primaryResourceType.ClrType, endpoint); + return ApplyTemplate(template, primaryResourceType, endpoint); } private static string GetTemplate(Type resourceClrType, ApiDescription endpoint) @@ -107,7 +105,7 @@ private static Type GetDocumentType(Type primaryResourceClrType, ApiDescription return type.IsConstructedGenericType ? type.GetGenericTypeDefinition() : null; } - private string ApplyTemplate(string operationIdTemplate, Type resourceClrType, ApiDescription endpoint) + private string ApplyTemplate(string operationIdTemplate, ResourceType resourceType, ApiDescription endpoint) { if (endpoint.RelativePath == null || endpoint.HttpMethod == null) { @@ -115,20 +113,20 @@ private string ApplyTemplate(string operationIdTemplate, Type resourceClrType, A } string method = endpoint.HttpMethod.ToLowerInvariant(); - string primaryResourceName = _formatter.FormatResourceName(resourceClrType).Singularize(); string relationshipName = operationIdTemplate.Contains("[RelationshipName]") ? endpoint.RelativePath.Split("/").Last() : string.Empty; // @formatter:wrap_chained_method_calls chop_always // @formatter:keep_existing_linebreaks true - string pascalCaseId = operationIdTemplate + string pascalCaseOperationId = operationIdTemplate .Replace("[Method]", method) - .Replace("[PrimaryResourceName]", primaryResourceName) - .Replace("[RelationshipName]", relationshipName); + .Replace("[PrimaryResourceName]", resourceType.PublicName.Singularize()) + .Replace("[RelationshipName]", relationshipName) + .ToPascalCase(); // @formatter:keep_existing_linebreaks restore // @formatter:wrap_chained_method_calls restore - return _namingPolicy != null ? _namingPolicy.ConvertName(pascalCaseId) : pascalCaseId; + return _namingPolicy != null ? _namingPolicy.ConvertName(pascalCaseOperationId) : pascalCaseOperationId; } } diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs index 6c1dcf1920..2eb3f13824 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using Humanizer; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents; @@ -10,42 +11,41 @@ internal sealed class JsonApiSchemaIdSelector { private static readonly IDictionary OpenTypeToSchemaTemplateMap = new Dictionary { - [typeof(ResourcePostRequestDocument<>)] = "###-post-request-document", - [typeof(ResourcePatchRequestDocument<>)] = "###-patch-request-document", - [typeof(ResourceObjectInPostRequest<>)] = "###-data-in-post-request", - [typeof(AttributesInPostRequest<>)] = "###-attributes-in-post-request", - [typeof(RelationshipsInPostRequest<>)] = "###-relationships-in-post-request", - [typeof(ResourceObjectInPatchRequest<>)] = "###-data-in-patch-request", - [typeof(AttributesInPatchRequest<>)] = "###-attributes-in-patch-request", - [typeof(RelationshipsInPatchRequest<>)] = "###-relationships-in-patch-request", - [typeof(ToOneRelationshipInRequest<>)] = "to-one-###-in-request", - [typeof(NullableToOneRelationshipInRequest<>)] = "nullable-to-one-###-in-request", - [typeof(ToManyRelationshipInRequest<>)] = "to-many-###-in-request", - [typeof(PrimaryResourceResponseDocument<>)] = "###-primary-response-document", - [typeof(SecondaryResourceResponseDocument<>)] = "###-secondary-response-document", - [typeof(NullableSecondaryResourceResponseDocument<>)] = "nullable-###-secondary-response-document", - [typeof(ResourceCollectionResponseDocument<>)] = "###-collection-response-document", - [typeof(ResourceIdentifierResponseDocument<>)] = "###-identifier-response-document", - [typeof(NullableResourceIdentifierResponseDocument<>)] = "nullable-###-identifier-response-document", - [typeof(ResourceIdentifierCollectionResponseDocument<>)] = "###-identifier-collection-response-document", - [typeof(ToOneRelationshipInResponse<>)] = "to-one-###-in-response", - [typeof(NullableToOneRelationshipInResponse<>)] = "nullable-to-one-###-in-response", - [typeof(ToManyRelationshipInResponse<>)] = "to-many-###-in-response", - [typeof(ResourceObjectInResponse<>)] = "###-data-in-response", - [typeof(AttributesInResponse<>)] = "###-attributes-in-response", - [typeof(RelationshipsInResponse<>)] = "###-relationships-in-response", - [typeof(ResourceIdentifierObject<>)] = "###-identifier" + [typeof(ResourcePostRequestDocument<>)] = "[ResourceName] Post Request Document", + [typeof(ResourcePatchRequestDocument<>)] = "[ResourceName] Patch Request Document", + [typeof(ResourceObjectInPostRequest<>)] = "[ResourceName] Data In Post Request", + [typeof(AttributesInPostRequest<>)] = "[ResourceName] Attributes In Post Request", + [typeof(RelationshipsInPostRequest<>)] = "[ResourceName] Relationships In Post Request", + [typeof(ResourceObjectInPatchRequest<>)] = "[ResourceName] Data In Patch Request", + [typeof(AttributesInPatchRequest<>)] = "[ResourceName] Attributes In Patch Request", + [typeof(RelationshipsInPatchRequest<>)] = "[ResourceName] Relationships In Patch Request", + [typeof(ToOneRelationshipInRequest<>)] = "To One [ResourceName] In Request", + [typeof(NullableToOneRelationshipInRequest<>)] = "Nullable To One [ResourceName] In Request", + [typeof(ToManyRelationshipInRequest<>)] = "To Many [ResourceName] In Request", + [typeof(PrimaryResourceResponseDocument<>)] = "[ResourceName] Primary Response Document", + [typeof(SecondaryResourceResponseDocument<>)] = "[ResourceName] Secondary Response Document", + [typeof(NullableSecondaryResourceResponseDocument<>)] = "Nullable [ResourceName] Secondary Response Document", + [typeof(ResourceCollectionResponseDocument<>)] = "[ResourceName] Collection Response Document", + [typeof(ResourceIdentifierResponseDocument<>)] = "[ResourceName] Identifier Response Document", + [typeof(NullableResourceIdentifierResponseDocument<>)] = "Nullable [ResourceName] Identifier Response Document", + [typeof(ResourceIdentifierCollectionResponseDocument<>)] = "[ResourceName] Identifier Collection Response Document", + [typeof(ToOneRelationshipInResponse<>)] = "To One [ResourceName] In Response", + [typeof(NullableToOneRelationshipInResponse<>)] = "Nullable To One [ResourceName] In Response", + [typeof(ToManyRelationshipInResponse<>)] = "To Many [ResourceName] In Response", + [typeof(ResourceObjectInResponse<>)] = "[ResourceName] Data In Response", + [typeof(AttributesInResponse<>)] = "[ResourceName] Attributes In Response", + [typeof(RelationshipsInResponse<>)] = "[ResourceName] Relationships In Response", + [typeof(ResourceIdentifierObject<>)] = "[ResourceName] Identifier" }; - private readonly ResourceNameFormatter _formatter; + private readonly JsonNamingPolicy? _namingPolicy; private readonly IResourceGraph _resourceGraph; - public JsonApiSchemaIdSelector(ResourceNameFormatter formatter, IResourceGraph resourceGraph) + public JsonApiSchemaIdSelector(JsonNamingPolicy? namingPolicy, IResourceGraph resourceGraph) { - ArgumentGuard.NotNull(formatter, nameof(formatter)); ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); - _formatter = formatter; + _namingPolicy = namingPolicy; _resourceGraph = resourceGraph; } @@ -62,14 +62,21 @@ public string GetSchemaId(Type type) if (type.IsConstructedGenericType && OpenTypeToSchemaTemplateMap.ContainsKey(type.GetGenericTypeDefinition())) { + Type openType = type.GetGenericTypeDefinition(); Type resourceClrType = type.GetGenericArguments().First(); - string resourceName = _formatter.FormatResourceName(resourceClrType).Singularize(); + resourceType = _resourceGraph.FindResourceType(resourceClrType); - string template = OpenTypeToSchemaTemplateMap[type.GetGenericTypeDefinition()]; - return template.Replace("###", resourceName); + if (resourceType == null) + { + throw new UnreachableCodeException(); + } + + string pascalCaseSchemaId = OpenTypeToSchemaTemplateMap[openType].Replace("[ResourceName]", resourceType.PublicName.Singularize()).ToPascalCase(); + + return _namingPolicy != null ? _namingPolicy.ConvertName(pascalCaseSchemaId) : pascalCaseSchemaId; } - // Used for a fixed set of types, such as jsonapi-object, links-in-many-resource-document etc. - return _formatter.FormatResourceName(type).Singularize(); + // Used for a fixed set of types, such as JsonApiObject, LinksInResourceCollectionDocument etc. + return _namingPolicy != null ? _namingPolicy.ConvertName(type.Name) : type.Name; } } diff --git a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs index 425617bdf8..55695102a2 100644 --- a/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/ServiceCollectionExtensions.cs @@ -55,7 +55,6 @@ private static void AddSwaggerGenerator(IServiceScope scope, IServiceCollection var resourceGraph = scope.ServiceProvider.GetRequiredService(); var jsonApiOptions = scope.ServiceProvider.GetRequiredService(); JsonNamingPolicy? namingPolicy = jsonApiOptions.SerializerOptions.PropertyNamingPolicy; - ResourceNameFormatter resourceNameFormatter = new(namingPolicy); AddSchemaGenerator(services); @@ -63,7 +62,7 @@ private static void AddSwaggerGenerator(IServiceScope scope, IServiceCollection { swaggerGenOptions.SupportNonNullableReferenceTypes(); SetOperationInfo(swaggerGenOptions, controllerResourceMapping, namingPolicy); - SetSchemaIdSelector(swaggerGenOptions, resourceGraph, resourceNameFormatter); + SetSchemaIdSelector(swaggerGenOptions, resourceGraph, namingPolicy); swaggerGenOptions.DocumentFilter(); setupSwaggerGenAction?.Invoke(swaggerGenOptions); @@ -101,9 +100,9 @@ private static IList GetOperationTags(ApiDescription description, IContr }; } - private static void SetSchemaIdSelector(SwaggerGenOptions swaggerGenOptions, IResourceGraph resourceGraph, ResourceNameFormatter resourceNameFormatter) + private static void SetSchemaIdSelector(SwaggerGenOptions swaggerGenOptions, IResourceGraph resourceGraph, JsonNamingPolicy? namingPolicy) { - JsonApiSchemaIdSelector jsonApiObjectSchemaSelector = new(resourceNameFormatter, resourceGraph); + JsonApiSchemaIdSelector jsonApiObjectSchemaSelector = new(namingPolicy, resourceGraph); swaggerGenOptions.CustomSchemaIds(type => jsonApiObjectSchemaSelector.GetSchemaId(type)); } diff --git a/src/JsonApiDotNetCore.OpenApi/StringExtensions.cs b/src/JsonApiDotNetCore.OpenApi/StringExtensions.cs new file mode 100644 index 0000000000..eff26f712c --- /dev/null +++ b/src/JsonApiDotNetCore.OpenApi/StringExtensions.cs @@ -0,0 +1,13 @@ +using System.Text.RegularExpressions; + +namespace JsonApiDotNetCore.OpenApi; + +internal static class StringExtensions +{ + private static readonly Regex Regex = new("(?:^|-|_| +)(.)", RegexOptions.Compiled); + + public static string ToPascalCase(this string source) + { + return Regex.Replace(source, match => match.Groups[1].Value.ToUpper()); + } +} diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs index 4f85affcfd..a0e7159a89 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs @@ -38,7 +38,7 @@ public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceG ArgumentGuard.NotNull(options, nameof(options)); _defaultSchemaGenerator = defaultSchemaGenerator; - _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(_schemaRepositoryAccessor); + _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(_schemaRepositoryAccessor, options.SerializerOptions.PropertyNamingPolicy); _resourceObjectSchemaGenerator = new ResourceObjectSchemaGenerator(defaultSchemaGenerator, resourceGraph, options, _schemaRepositoryAccessor); } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs index c8e0fe79db..d9bedc343c 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/NullableReferenceSchemaGenerator.cs @@ -1,20 +1,27 @@ +using System.Text.Json; using Microsoft.OpenApi.Models; namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class NullableReferenceSchemaGenerator { - private static readonly NullableReferenceSchemaStrategy NullableReferenceStrategy = + private const string PascalCaseNullableSchemaReferenceId = "NullValue"; + + private readonly NullableReferenceSchemaStrategy _nullableReferenceStrategy = Enum.Parse(NullableReferenceSchemaStrategy.Implicit.ToString()); - private static OpenApiSchema? _referenceSchemaForNullValue; private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; + private readonly string _nullableSchemaReferenceId; + + private OpenApiSchema? _referenceSchemaForExplicitNullValue; - public NullableReferenceSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor) + public NullableReferenceSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor, JsonNamingPolicy? namingPolicy) { ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); _schemaRepositoryAccessor = schemaRepositoryAccessor; + + _nullableSchemaReferenceId = namingPolicy != null ? namingPolicy.ConvertName(PascalCaseNullableSchemaReferenceId) : PascalCaseNullableSchemaReferenceId; } public OpenApiSchema GenerateSchema(OpenApiSchema referenceSchema) @@ -26,20 +33,13 @@ public OpenApiSchema GenerateSchema(OpenApiSchema referenceSchema) OneOf = new List { referenceSchema, - GetNullableReferenceSchema() + _nullableReferenceStrategy == NullableReferenceSchemaStrategy.Explicit ? GetExplicitNullSchema() : GetImplicitNullSchema() } }; } - private OpenApiSchema GetNullableReferenceSchema() - { - return NullableReferenceStrategy == NullableReferenceSchemaStrategy.Explicit - ? GetNullableReferenceSchemaUsingExplicitNullType() - : GetNullableReferenceSchemaUsingImplicitNullType(); - } - // This approach is supported in OAS starting from v3.1. See https://github.com/OAI/OpenAPI-Specification/issues/1368#issuecomment-580103688 - private static OpenApiSchema GetNullableReferenceSchemaUsingExplicitNullType() + private static OpenApiSchema GetExplicitNullSchema() { return new OpenApiSchema { @@ -48,47 +48,57 @@ private static OpenApiSchema GetNullableReferenceSchemaUsingExplicitNullType() } // This approach is supported starting from OAS v3.0. See https://github.com/OAI/OpenAPI-Specification/issues/1368#issuecomment-487314681 - private OpenApiSchema GetNullableReferenceSchemaUsingImplicitNullType() + private OpenApiSchema GetImplicitNullSchema() { - if (_referenceSchemaForNullValue != null) + EnsureFullSchemaForNullValueExists(); + + return _referenceSchemaForExplicitNullValue ??= new OpenApiSchema { - return _referenceSchemaForNullValue; - } + Reference = new OpenApiReference + { + Id = _nullableSchemaReferenceId, + Type = ReferenceType.Schema + } + }; + } - var fullSchemaForNullValue = new OpenApiSchema + private void EnsureFullSchemaForNullValueExists() + { + if (!_schemaRepositoryAccessor.Current.Schemas.ContainsKey(_nullableSchemaReferenceId)) { - Nullable = true, - Not = new OpenApiSchema + var fullSchemaForNullValue = new OpenApiSchema { - AnyOf = new List + Nullable = true, + Not = new OpenApiSchema { - new() - { - Type = "string" - }, - new() - { - Type = "number" - }, - new() - { - Type = "boolean" - }, - new() + AnyOf = new List { - Type = "object" + new() + { + Type = "string" + }, + new() + { + Type = "number" + }, + new() + { + Type = "boolean" + }, + new() + { + Type = "object" + }, + new() + { + Type = "array" + } }, - new() - { - Type = "array" - } - }, - Items = new OpenApiSchema() - } - }; - - _referenceSchemaForNullValue = _schemaRepositoryAccessor.Current.AddDefinition("null-value", fullSchemaForNullValue); + Items = new OpenApiSchema() + } + }; - return _referenceSchemaForNullValue; + _schemaRepositoryAccessor.Current.AddDefinition(_nullableSchemaReferenceId, fullSchemaForNullValue); + } } } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 50d56c0965..ef8c705424 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Reflection; +using System.Text.Json; using JsonApiDotNetCore.OpenApi.JsonApiObjects; using JsonApiDotNetCore.OpenApi.JsonApiObjects.Relationships; using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects; @@ -11,8 +12,6 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class ResourceFieldObjectSchemaBuilder { - private static readonly SchemaRepository ResourceSchemaRepository = new(); - private static readonly Type[] RelationshipInResponseOpenTypes = { typeof(ToOneRelationshipInResponse<>), @@ -24,11 +23,12 @@ internal sealed class ResourceFieldObjectSchemaBuilder private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; private readonly SchemaGenerator _defaultSchemaGenerator; private readonly ResourceTypeSchemaGenerator _resourceTypeSchemaGenerator; + private readonly SchemaRepository _resourceSchemaRepository = new(); private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator; private readonly IDictionary _schemasForResourceFields; public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor, - SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator) + SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator, JsonNamingPolicy? namingPolicy) { ArgumentGuard.NotNull(resourceTypeInfo, nameof(resourceTypeInfo)); ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); @@ -40,18 +40,18 @@ public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISche _defaultSchemaGenerator = defaultSchemaGenerator; _resourceTypeSchemaGenerator = resourceTypeSchemaGenerator; - _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor); + _nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor, namingPolicy); _schemasForResourceFields = GetFieldSchemas(); } private IDictionary GetFieldSchemas() { - if (!ResourceSchemaRepository.TryLookupByType(_resourceTypeInfo.ResourceType.ClrType, out OpenApiSchema referenceSchemaForResource)) + if (!_resourceSchemaRepository.TryLookupByType(_resourceTypeInfo.ResourceType.ClrType, out OpenApiSchema referenceSchemaForResource)) { - referenceSchemaForResource = _defaultSchemaGenerator.GenerateSchema(_resourceTypeInfo.ResourceType.ClrType, ResourceSchemaRepository); + referenceSchemaForResource = _defaultSchemaGenerator.GenerateSchema(_resourceTypeInfo.ResourceType.ClrType, _resourceSchemaRepository); } - OpenApiSchema fullSchemaForResource = ResourceSchemaRepository.Schemas[referenceSchemaForResource.Reference.Id]; + OpenApiSchema fullSchemaForResource = _resourceSchemaRepository.Schemas[referenceSchemaForResource.Reference.Id]; return fullSchemaForResource.Properties; } @@ -96,7 +96,7 @@ private void AddAttributeSchemaToResourceObject(AttrAttribute attribute, OpenApi private void ExposeSchema(OpenApiReference openApiReference, Type typeRepresentedBySchema) { - OpenApiSchema fullSchema = ResourceSchemaRepository.Schemas[openApiReference.Id]; + OpenApiSchema fullSchema = _resourceSchemaRepository.Schemas[openApiReference.Id]; _schemaRepositoryAccessor.Current.AddDefinition(openApiReference.Id, fullSchema); _schemaRepositoryAccessor.Current.RegisterType(typeRepresentedBySchema, openApiReference.Id); } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 540517b59c..616b98ef63 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -26,11 +26,12 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe _resourceGraph = resourceGraph; _schemaRepositoryAccessor = schemaRepositoryAccessor; - _resourceTypeSchemaGenerator = new ResourceTypeSchemaGenerator(schemaRepositoryAccessor, resourceGraph); + _resourceTypeSchemaGenerator = new ResourceTypeSchemaGenerator(schemaRepositoryAccessor, resourceGraph, options.SerializerOptions.PropertyNamingPolicy); + _allowClientGeneratedIds = options.AllowClientGeneratedIds; _resourceFieldObjectSchemaBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, - defaultSchemaGenerator, _resourceTypeSchemaGenerator); + defaultSchemaGenerator, _resourceTypeSchemaGenerator, options.SerializerOptions.PropertyNamingPolicy); } public OpenApiSchema GenerateSchema(Type resourceObjectType) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs index d110511880..9113f3b359 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceTypeSchemaGenerator.cs @@ -1,3 +1,5 @@ +using System.Text.Json; +using Humanizer; using JsonApiDotNetCore.Configuration; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; @@ -6,17 +8,20 @@ namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; internal sealed class ResourceTypeSchemaGenerator { + private const string ResourceTypeSchemaIdTemplate = "[ResourceName] Resource Type"; private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor; private readonly IResourceGraph _resourceGraph; + private readonly JsonNamingPolicy? _namingPolicy; private readonly Dictionary _resourceClrTypeSchemaCache = new(); - public ResourceTypeSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor, IResourceGraph resourceGraph) + public ResourceTypeSchemaGenerator(ISchemaRepositoryAccessor schemaRepositoryAccessor, IResourceGraph resourceGraph, JsonNamingPolicy? namingPolicy) { ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor)); ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph)); _schemaRepositoryAccessor = schemaRepositoryAccessor; _resourceGraph = resourceGraph; + _namingPolicy = namingPolicy; } public OpenApiSchema Get(Type resourceClrType) @@ -39,11 +44,13 @@ public OpenApiSchema Get(Type resourceClrType) } }; + string schemaId = GetSchemaId(resourceType); + referenceSchema = new OpenApiSchema { Reference = new OpenApiReference { - Id = $"{resourceType.PublicName}-resource-type", + Id = schemaId, Type = ReferenceType.Schema } }; @@ -53,4 +60,11 @@ public OpenApiSchema Get(Type resourceClrType) return referenceSchema; } + + private string GetSchemaId(ResourceType resourceType) + { + string pascalCaseSchemaId = ResourceTypeSchemaIdTemplate.Replace("[ResourceName]", resourceType.PublicName.Singularize()).ToPascalCase(); + + return _namingPolicy != null ? _namingPolicy.ConvertName(pascalCaseSchemaId) : pascalCaseSchemaId; + } } diff --git a/src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs b/src/JsonApiDotNetCore/Serialization/Objects/JsonapiObject.cs similarity index 100% rename from src/JsonApiDotNetCore/Serialization/Objects/JsonApiObject.cs rename to src/JsonApiDotNetCore/Serialization/Objects/JsonapiObject.cs diff --git a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs b/test/OpenApiClientTests/LegacyClient/ApiResponse.cs index cb00e862a4..d009d2acee 100644 --- a/test/OpenApiClientTests/LegacyClient/ApiResponse.cs +++ b/test/OpenApiClientTests/LegacyClient/ApiResponse.cs @@ -1,5 +1,5 @@ using JsonApiDotNetCore.OpenApi.Client; -using OpenApiClientTests.LegacyClient.GeneratedCode; +using JsonApiDotNetCore.OpenApi.Client.Exceptions; #pragma warning disable AV1008 // Class should not be static diff --git a/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs b/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs index c164aafd0f..4582f9578d 100644 --- a/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ClientAttributeRegistrationLifeTimeTests.cs @@ -22,7 +22,7 @@ public async Task Disposed_attribute_registration_for_document_does_not_affect_r Data = new AirplaneDataInPatchRequest { Id = airplaneId, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -64,7 +64,7 @@ public async Task Attribute_registration_can_be_used_for_multiple_requests() Data = new AirplaneDataInPatchRequest { Id = airplaneId, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest { AirtimeInHours = 100 @@ -111,7 +111,7 @@ public async Task Request_is_unaffected_by_attribute_registration_for_different_ Data = new AirplaneDataInPatchRequest { Id = airplaneId1, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -123,7 +123,7 @@ public async Task Request_is_unaffected_by_attribute_registration_for_different_ Data = new AirplaneDataInPatchRequest { Id = airplaneId2, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -166,7 +166,7 @@ public async Task Attribute_values_can_be_changed_after_attribute_registration() Data = new AirplaneDataInPatchRequest { Id = airplaneId, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest { IsInMaintenance = true @@ -209,7 +209,7 @@ public async Task Attribute_registration_is_unaffected_by_successive_attribute_r Data = new AirplaneDataInPatchRequest { Id = airplaneId1, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -218,7 +218,7 @@ public async Task Attribute_registration_is_unaffected_by_successive_attribute_r { Data = new AirplaneDataInPostRequest { - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPostRequest() } }; @@ -260,7 +260,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att Data = new AirplaneDataInPatchRequest { Id = airplaneId1, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -278,7 +278,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att Data = new AirplaneDataInPatchRequest { Id = airplaneId2, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest { ManufacturedInCity = "Everett" @@ -319,7 +319,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att { Data = new AirplaneDataInPostRequest { - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPostRequest() } }; @@ -337,7 +337,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_disposed_att Data = new AirplaneDataInPatchRequest { Id = airplaneId, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest { ManufacturedInCity = "Everett" @@ -381,7 +381,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_attribute_re Data = new AirplaneDataInPatchRequest { Id = airplaneId1, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; @@ -393,7 +393,7 @@ public async Task Attribute_registration_is_unaffected_by_preceding_attribute_re Data = new AirplaneDataInPatchRequest { Id = airplaneId2, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest() } }; diff --git a/test/OpenApiClientTests/LegacyClient/RequestTests.cs b/test/OpenApiClientTests/LegacyClient/RequestTests.cs index 831dfda3f7..e03e8f1015 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestTests.cs @@ -63,7 +63,7 @@ public async Task Partial_posting_resource_with_selected_relationships_produces_ { Data = new FlightDataInPostRequest { - Type = FlightsResourceType.Flights, + Type = FlightResourceType.Flights, Relationships = new FlightRelationshipsInPostRequest { Purser = new ToOneFlightAttendantInRequest @@ -71,7 +71,7 @@ public async Task Partial_posting_resource_with_selected_relationships_produces_ Data = new FlightAttendantIdentifier { Id = "bBJHu", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } }, BackupPurser = new NullableToOneFlightAttendantInRequest @@ -79,7 +79,7 @@ public async Task Partial_posting_resource_with_selected_relationships_produces_ Data = new FlightAttendantIdentifier { Id = "NInmX", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } } @@ -143,7 +143,7 @@ public async Task Partial_posting_resource_produces_expected_request() { Data = new AirplaneDataInPostRequest { - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPostRequest { Name = name, @@ -195,7 +195,7 @@ public async Task Partial_patching_resource_produces_expected_request() Data = new AirplaneDataInPatchRequest { Id = airplaneId, - Type = AirplanesResourceType.Airplanes, + Type = AirplaneResourceType.Airplanes, Attributes = new AirplaneAttributesInPatchRequest { LastServicedAt = lastServicedAt @@ -326,7 +326,7 @@ public async Task Patching_ToOne_relationship_produces_expected_request() Data = new FlightAttendantIdentifier { Id = "bBJHu", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } }; @@ -384,12 +384,12 @@ public async Task Posting_ToMany_relationship_produces_expected_request() { new() { - Type = FlightAttendantsResourceType.FlightAttendants, + Type = FlightAttendantResourceType.FlightAttendants, Id = "bBJHu" }, new() { - Type = FlightAttendantsResourceType.FlightAttendants, + Type = FlightAttendantResourceType.FlightAttendants, Id = "NInmX" } } @@ -436,12 +436,12 @@ public async Task Patching_ToMany_relationship_produces_expected_request() new() { Id = "bBJHu", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants }, new() { Id = "NInmX", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } }; @@ -487,12 +487,12 @@ public async Task Deleting_ToMany_relationship_produces_expected_request() new() { Id = "bBJHu", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants }, new() { Id = "NInmX", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } }; diff --git a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs index ee782c0adf..10f86adca5 100644 --- a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs @@ -2,6 +2,7 @@ using System.Net; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client.Exceptions; using OpenApiClientTests.LegacyClient.GeneratedCode; using Xunit; @@ -108,7 +109,7 @@ public async Task Getting_resource_collection_translates_response() FlightDataInResponse flight = document.Data.First(); flight.Id.Should().Be(flightId); - flight.Type.Should().Be(FlightsResourceType.Flights); + flight.Type.Should().Be(FlightResourceType.Flights); flight.Links.Self.Should().Be(flightResourceLink); flight.Meta.Should().HaveCount(1); flight.Meta["docs"].Should().Be(flightMetaValue); @@ -276,7 +277,7 @@ public async Task Posting_resource_translates_response() { Data = new FlightDataInPostRequest { - Type = FlightsResourceType.Flights, + Type = FlightResourceType.Flights, Relationships = new FlightRelationshipsInPostRequest { Purser = new ToOneFlightAttendantInRequest @@ -284,7 +285,7 @@ public async Task Posting_resource_translates_response() Data = new FlightAttendantIdentifier { Id = flightAttendantId, - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } } @@ -297,7 +298,7 @@ public async Task Posting_resource_translates_response() document.Data.Relationships.Purser.Data.Id.Should().Be(flightAttendantId); document.Data.Relationships.CabinCrewMembers.Data.Should().HaveCount(1); document.Data.Relationships.CabinCrewMembers.Data.First().Id.Should().Be(flightAttendantId); - document.Data.Relationships.CabinCrewMembers.Data.First().Type.Should().Be(FlightAttendantsResourceType.FlightAttendants); + document.Data.Relationships.CabinCrewMembers.Data.First().Type.Should().Be(FlightAttendantResourceType.FlightAttendants); document.Data.Relationships.Passengers.Data.Should().BeEmpty(); } @@ -329,12 +330,12 @@ public async Task Patching_resource_with_side_effects_translates_response() Data = new FlightDataInPatchRequest { Id = flightId, - Type = FlightsResourceType.Flights + Type = FlightResourceType.Flights } }); // Assert - document.Data.Type.Should().Be(FlightsResourceType.Flights); + document.Data.Type.Should().Be(FlightResourceType.Flights); document.Data.Attributes.Should().BeNull(); document.Data.Relationships.Should().BeNull(); } @@ -354,7 +355,7 @@ public async Task Patching_resource_without_side_effects_translates_response() Data = new FlightDataInPatchRequest { Id = flightId, - Type = FlightsResourceType.Flights + Type = FlightResourceType.Flights } })); @@ -537,7 +538,7 @@ public async Task Getting_ToOne_relationship_translates_response() // Assert document.Data.Should().NotBeNull(); document.Data.Id.Should().Be(purserId); - document.Data.Type.Should().Be(FlightAttendantsResourceType.FlightAttendants); + document.Data.Type.Should().Be(FlightAttendantResourceType.FlightAttendants); } [Fact] @@ -553,7 +554,7 @@ public async Task Patching_ToOne_relationship_translates_response() Data = new FlightAttendantIdentifier { Id = "Adk2a", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } }); } @@ -591,9 +592,9 @@ public async Task Getting_ToMany_relationship_translates_response() // Assert document.Data.Should().HaveCount(2); document.Data.First().Id.Should().Be(flightAttendantId1); - document.Data.First().Type.Should().Be(FlightAttendantsResourceType.FlightAttendants); + document.Data.First().Type.Should().Be(FlightAttendantResourceType.FlightAttendants); document.Data.Last().Id.Should().Be(flightAttendantId2); - document.Data.Last().Type.Should().Be(FlightAttendantsResourceType.FlightAttendants); + document.Data.Last().Type.Should().Be(FlightAttendantResourceType.FlightAttendants); } [Fact] @@ -611,12 +612,12 @@ public async Task Posting_ToMany_relationship_produces_empty_response() new() { Id = "Adk2a", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants }, new() { Id = "Un37k", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } }); @@ -640,12 +641,12 @@ public async Task Patching_ToMany_relationship_produces_empty_response() new() { Id = "Adk2a", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants }, new() { Id = "Un37k", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } }); @@ -669,12 +670,12 @@ public async Task Deleting_ToMany_relationship_produces_empty_response() new() { Id = "Adk2a", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants }, new() { Id = "Un37k", - Type = FlightAttendantsResourceType.FlightAttendants + Type = FlightAttendantResourceType.FlightAttendants } } }); diff --git a/test/OpenApiClientTests/LegacyClient/swagger.g.json b/test/OpenApiClientTests/LegacyClient/swagger.g.json new file mode 100644 index 0000000000..03575fe1d2 --- /dev/null +++ b/test/OpenApiClientTests/LegacyClient/swagger.g.json @@ -0,0 +1,3363 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/api/v1/airplanes": { + "get": { + "tags": [ + "airplanes" + ], + "operationId": "get-airplane-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "airplanes" + ], + "operationId": "head-airplane-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "airplanes" + ], + "operationId": "post-airplane", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-post-request-document" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/airplanes/{id}": { + "get": { + "tags": [ + "airplanes" + ], + "operationId": "get-airplane", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-primary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "airplanes" + ], + "operationId": "head-airplane", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-primary-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "airplanes" + ], + "operationId": "patch-airplane", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-patch-request-document" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/airplane-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "airplanes" + ], + "operationId": "delete-airplane", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/airplanes/{id}/flights": { + "get": { + "tags": [ + "airplanes" + ], + "operationId": "get-airplane-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "airplanes" + ], + "operationId": "head-airplane-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + } + }, + "/api/v1/airplanes/{id}/relationships/flights": { + "get": { + "tags": [ + "airplanes" + ], + "operationId": "get-airplane-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "airplanes" + ], + "operationId": "head-airplane-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "airplanes" + ], + "operationId": "post-airplane-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "airplanes" + ], + "operationId": "patch-airplane-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "airplanes" + ], + "operationId": "delete-airplane-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flight-attendants": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flight-attendants" + ], + "operationId": "post-flight-attendant", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-post-request-document" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flight-attendants/{id}": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-primary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-primary-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "flight-attendants" + ], + "operationId": "patch-flight-attendant", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-patch-request-document" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flight-attendants" + ], + "operationId": "delete-flight-attendant", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flight-attendants/{id}/purser-on-flights": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant-purser-on-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant-purser-on-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + } + }, + "/api/v1/flight-attendants/{id}/relationships/purser-on-flights": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant-purser-on-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant-purser-on-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flight-attendants" + ], + "operationId": "post-flight-attendant-purser-on-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "flight-attendants" + ], + "operationId": "patch-flight-attendant-purser-on-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flight-attendants" + ], + "operationId": "delete-flight-attendant-purser-on-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flight-attendants/{id}/scheduled-for-flights": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant-scheduled-for-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant-scheduled-for-flights", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + } + }, + "/api/v1/flight-attendants/{id}/relationships/scheduled-for-flights": { + "get": { + "tags": [ + "flight-attendants" + ], + "operationId": "get-flight-attendant-scheduled-for-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flight-attendants" + ], + "operationId": "head-flight-attendant-scheduled-for-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flight-attendants" + ], + "operationId": "post-flight-attendant-scheduled-for-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "flight-attendants" + ], + "operationId": "patch-flight-attendant-scheduled-for-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flight-attendants" + ], + "operationId": "delete-flight-attendant-scheduled-for-flights-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flights" + ], + "operationId": "post-flight", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-post-request-document" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights/{id}": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-primary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-primary-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "flights" + ], + "operationId": "patch-flight", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-patch-request-document" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flights" + ], + "operationId": "delete-flight", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights/{id}/backup-purser": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-backup-purser", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-flight-attendant-secondary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-backup-purser", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-flight-attendant-secondary-response-document" + } + } + } + } + } + } + }, + "/api/v1/flights/{id}/relationships/backup-purser": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-backup-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-flight-attendant-identifier-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-backup-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-flight-attendant-identifier-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "flights" + ], + "operationId": "patch-flight-backup-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-to-one-flight-attendant-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights/{id}/cabin-crew-members": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-cabin-crew-members", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-cabin-crew-members", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-collection-response-document" + } + } + } + } + } + } + }, + "/api/v1/flights/{id}/relationships/cabin-crew-members": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-cabin-crew-members-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-cabin-crew-members-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flights" + ], + "operationId": "post-flight-cabin-crew-members-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "flights" + ], + "operationId": "patch-flight-cabin-crew-members-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flights" + ], + "operationId": "delete-flight-cabin-crew-members-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights/{id}/passengers": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-passengers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/passenger-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-passengers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/passenger-collection-response-document" + } + } + } + } + } + } + }, + "/api/v1/flights/{id}/relationships/passengers": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-passengers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/passenger-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-passengers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/passenger-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "flights" + ], + "operationId": "post-flight-passengers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-passenger-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "flights" + ], + "operationId": "patch-flight-passengers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-passenger-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "flights" + ], + "operationId": "delete-flight-passengers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-passenger-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/api/v1/flights/{id}/purser": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-purser", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-secondary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-purser", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-secondary-response-document" + } + } + } + } + } + } + }, + "/api/v1/flights/{id}/relationships/purser": { + "get": { + "tags": [ + "flights" + ], + "operationId": "get-flight-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-identifier-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "flights" + ], + "operationId": "head-flight-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/flight-attendant-identifier-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "flights" + ], + "operationId": "patch-flight-purser-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-one-flight-attendant-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "components": { + "schemas": { + "aircraft-kind": { + "enum": [ + "Turboprops", + "LightJet", + "MidSizeJet", + "JumboJet" + ], + "type": "string" + }, + "airline": { + "enum": [ + "DeltaAirLines", + "LufthansaGroup", + "AirFranceKlm" + ], + "type": "string" + }, + "airplane-attributes-in-patch-request": { + "type": "object", + "properties": { + "name": { + "maxLength": 255, + "type": "string" + }, + "serial-number": { + "maxLength": 16, + "type": "string", + "nullable": true + }, + "airtime-in-hours": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "last-serviced-at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "is-in-maintenance": { + "type": "boolean" + }, + "manufactured-in-city": { + "maxLength": 85, + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "airplane-attributes-in-post-request": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "maxLength": 255, + "type": "string" + }, + "serial-number": { + "maxLength": 16, + "type": "string", + "nullable": true + }, + "airtime-in-hours": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "last-serviced-at": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "additionalProperties": false + }, + "airplane-attributes-in-response": { + "type": "object", + "properties": { + "name": { + "maxLength": 255, + "type": "string" + }, + "serial-number": { + "maxLength": 16, + "type": "string", + "nullable": true + }, + "airtime-in-hours": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "last-serviced-at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "manufactured-at": { + "type": "string", + "format": "date-time" + }, + "is-in-maintenance": { + "type": "boolean" + }, + "manufactured-in-city": { + "maxLength": 85, + "type": "string", + "nullable": true + }, + "kind": { + "$ref": "#/components/schemas/aircraft-kind" + } + }, + "additionalProperties": false + }, + "airplane-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/airplane-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "airplane-data-in-patch-request": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/airplane-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/airplane-attributes-in-patch-request" + }, + "relationships": { + "$ref": "#/components/schemas/airplane-relationships-in-patch-request" + } + }, + "additionalProperties": false + }, + "airplane-data-in-post-request": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/airplane-resource-type" + }, + "attributes": { + "$ref": "#/components/schemas/airplane-attributes-in-post-request" + }, + "relationships": { + "$ref": "#/components/schemas/airplane-relationships-in-post-request" + } + }, + "additionalProperties": false + }, + "airplane-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/airplane-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/airplane-attributes-in-response" + }, + "relationships": { + "$ref": "#/components/schemas/airplane-relationships-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "airplane-patch-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/airplane-data-in-patch-request" + } + }, + "additionalProperties": false + }, + "airplane-post-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/airplane-data-in-post-request" + } + }, + "additionalProperties": false + }, + "airplane-primary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/airplane-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "airplane-relationships-in-patch-request": { + "type": "object", + "properties": { + "flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + }, + "additionalProperties": false + }, + "airplane-relationships-in-post-request": { + "type": "object", + "properties": { + "flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + }, + "additionalProperties": false + }, + "airplane-relationships-in-response": { + "type": "object", + "properties": { + "flights": { + "$ref": "#/components/schemas/to-many-flight-in-response" + } + }, + "additionalProperties": false + }, + "airplane-resource-type": { + "enum": [ + "airplanes" + ], + "type": "string" + }, + "cabin-area": { + "enum": [ + "FirstClass", + "BusinessClass", + "EconomyClass" + ], + "type": "string" + }, + "flight-attendant-attributes-in-patch-request": { + "type": "object", + "properties": { + "email-address": { + "type": "string", + "format": "email" + }, + "age": { + "maximum": 75, + "minimum": 18, + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "flight-attendant-attributes-in-post-request": { + "required": [ + "email-address", + "profile-image-url" + ], + "type": "object", + "properties": { + "email-address": { + "type": "string", + "format": "email" + }, + "age": { + "maximum": 75, + "minimum": 18, + "type": "integer", + "format": "int32" + }, + "profile-image-url": { + "type": "string", + "format": "uri" + } + }, + "additionalProperties": false + }, + "flight-attendant-attributes-in-response": { + "type": "object", + "properties": { + "email-address": { + "type": "string", + "format": "email" + }, + "age": { + "maximum": 75, + "minimum": 18, + "type": "integer", + "format": "int32" + }, + "profile-image-url": { + "type": "string", + "format": "uri" + }, + "distance-traveled-in-kilometers": { + "type": "integer", + "format": "int64" + } + }, + "additionalProperties": false + }, + "flight-attendant-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-attendant-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "flight-attendant-data-in-patch-request": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-attendant-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/flight-attendant-attributes-in-patch-request" + }, + "relationships": { + "$ref": "#/components/schemas/flight-attendant-relationships-in-patch-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-data-in-post-request": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-attendant-resource-type" + }, + "attributes": { + "$ref": "#/components/schemas/flight-attendant-attributes-in-post-request" + }, + "relationships": { + "$ref": "#/components/schemas/flight-attendant-relationships-in-post-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-attendant-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/flight-attendant-attributes-in-response" + }, + "relationships": { + "$ref": "#/components/schemas/flight-attendant-relationships-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "flight-attendant-identifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-attendant-resource-type" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "flight-attendant-identifier-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-attendant-identifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-collection-document" + } + }, + "additionalProperties": false + }, + "flight-attendant-identifier-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-identifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-document" + } + }, + "additionalProperties": false + }, + "flight-attendant-patch-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-data-in-patch-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-post-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-data-in-post-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-primary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "flight-attendant-relationships-in-patch-request": { + "type": "object", + "properties": { + "scheduled-for-flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + }, + "purser-on-flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-relationships-in-post-request": { + "type": "object", + "properties": { + "scheduled-for-flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + }, + "purser-on-flights": { + "$ref": "#/components/schemas/to-many-flight-in-request" + } + }, + "additionalProperties": false + }, + "flight-attendant-relationships-in-response": { + "type": "object", + "properties": { + "scheduled-for-flights": { + "$ref": "#/components/schemas/to-many-flight-in-response" + }, + "purser-on-flights": { + "$ref": "#/components/schemas/to-many-flight-in-response" + } + }, + "additionalProperties": false + }, + "flight-attendant-resource-type": { + "enum": [ + "flight-attendants" + ], + "type": "string" + }, + "flight-attendant-secondary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "flight-attributes-in-patch-request": { + "type": "object", + "properties": { + "final-destination": { + "maxLength": 40, + "type": "string" + }, + "stop-over-destination": { + "maxLength": 2000, + "type": "string", + "nullable": true + }, + "operated-by": { + "$ref": "#/components/schemas/airline" + } + }, + "additionalProperties": false + }, + "flight-attributes-in-response": { + "type": "object", + "properties": { + "final-destination": { + "maxLength": 40, + "type": "string" + }, + "stop-over-destination": { + "maxLength": 2000, + "type": "string", + "nullable": true + }, + "operated-by": { + "$ref": "#/components/schemas/airline" + }, + "departs-at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "arrives-at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "services-on-board": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "flight-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "flight-data-in-patch-request": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/flight-attributes-in-patch-request" + }, + "relationships": { + "$ref": "#/components/schemas/flight-relationships-in-patch-request" + } + }, + "additionalProperties": false + }, + "flight-data-in-post-request": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-resource-type" + }, + "relationships": { + "$ref": "#/components/schemas/flight-relationships-in-post-request" + } + }, + "additionalProperties": false + }, + "flight-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/flight-attributes-in-response" + }, + "relationships": { + "$ref": "#/components/schemas/flight-relationships-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "flight-identifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/flight-resource-type" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "flight-identifier-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-identifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-collection-document" + } + }, + "additionalProperties": false + }, + "flight-patch-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-data-in-patch-request" + } + }, + "additionalProperties": false + }, + "flight-post-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-data-in-post-request" + } + }, + "additionalProperties": false + }, + "flight-primary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "flight-relationships-in-patch-request": { + "type": "object", + "properties": { + "cabin-crew-members": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-request" + }, + "purser": { + "$ref": "#/components/schemas/to-one-flight-attendant-in-request" + }, + "backup-purser": { + "$ref": "#/components/schemas/nullable-to-one-flight-attendant-in-request" + }, + "passengers": { + "$ref": "#/components/schemas/to-many-passenger-in-request" + } + }, + "additionalProperties": false + }, + "flight-relationships-in-post-request": { + "required": [ + "purser" + ], + "type": "object", + "properties": { + "cabin-crew-members": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-request" + }, + "purser": { + "$ref": "#/components/schemas/to-one-flight-attendant-in-request" + }, + "backup-purser": { + "$ref": "#/components/schemas/nullable-to-one-flight-attendant-in-request" + }, + "passengers": { + "$ref": "#/components/schemas/to-many-passenger-in-request" + } + }, + "additionalProperties": false + }, + "flight-relationships-in-response": { + "type": "object", + "properties": { + "cabin-crew-members": { + "$ref": "#/components/schemas/to-many-flight-attendant-in-response" + }, + "purser": { + "$ref": "#/components/schemas/to-one-flight-attendant-in-response" + }, + "backup-purser": { + "$ref": "#/components/schemas/nullable-to-one-flight-attendant-in-response" + }, + "passengers": { + "$ref": "#/components/schemas/to-many-passenger-in-response" + } + }, + "additionalProperties": false + }, + "flight-resource-type": { + "enum": [ + "flights" + ], + "type": "string" + }, + "jsonapi-object": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "links-in-relationship-object": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-collection-document": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-document": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-identifier-collection-document": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-identifier-document": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-object": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "null-value": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullable-flight-attendant-identifier-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/flight-attendant-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-document" + } + }, + "additionalProperties": false + }, + "nullable-flight-attendant-secondary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/flight-attendant-data-in-response" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "nullable-to-one-flight-attendant-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/flight-attendant-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + } + }, + "additionalProperties": false + }, + "nullable-to-one-flight-attendant-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/flight-attendant-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "passenger-attributes-in-response": { + "type": "object", + "properties": { + "full-name": { + "type": "string", + "nullable": true + }, + "cabin-area": { + "$ref": "#/components/schemas/cabin-area" + } + }, + "additionalProperties": false + }, + "passenger-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/passenger-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "passenger-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/passenger-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/passenger-attributes-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "passenger-identifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/passenger-resource-type" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "passenger-identifier-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/passenger-identifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-collection-document" + } + }, + "additionalProperties": false + }, + "passenger-resource-type": { + "enum": [ + "passengers" + ], + "type": "string" + }, + "to-many-flight-attendant-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-attendant-identifier" + } + } + }, + "additionalProperties": false + }, + "to-many-flight-attendant-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-attendant-identifier" + } + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "to-many-flight-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-identifier" + } + } + }, + "additionalProperties": false + }, + "to-many-flight-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/flight-identifier" + } + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "to-many-passenger-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/passenger-identifier" + } + } + }, + "additionalProperties": false + }, + "to-many-passenger-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/passenger-identifier" + } + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "to-one-flight-attendant-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-identifier" + } + }, + "additionalProperties": false + }, + "to-one-flight-attendant-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/flight-attendant-identifier" + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/NamingConventions/CamelCase/GeneratedTypesTests.cs b/test/OpenApiClientTests/NamingConventions/CamelCase/GeneratedTypesTests.cs new file mode 100644 index 0000000000..003c452c98 --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/CamelCase/GeneratedTypesTests.cs @@ -0,0 +1,77 @@ +using OpenApiClientTests.NamingConventions.CamelCase.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.NamingConventions.CamelCase; + +public sealed class GeneratedTypesTests +{ + [Fact] + public void Generated_code_is_named_as_expected() + { + _ = nameof(CamelCaseClient.GetSupermarketCollectionAsync); + _ = nameof(CamelCaseClient.GetSupermarketCollectionAsync); + _ = nameof(CamelCaseClient.PostSupermarketAsync); + _ = nameof(CamelCaseClient.GetSupermarketAsync); + _ = nameof(CamelCaseClient.GetSupermarketAsync); + _ = nameof(CamelCaseClient.PatchSupermarketAsync); + _ = nameof(CamelCaseClient.DeleteSupermarketAsync); + _ = nameof(CamelCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(CamelCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(CamelCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(CamelCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(CamelCaseClient.PatchSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(CamelCaseClient.GetSupermarketCashiersAsync); + _ = nameof(CamelCaseClient.GetSupermarketCashiersAsync); + _ = nameof(CamelCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(CamelCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(CamelCaseClient.PostSupermarketCashiersRelationshipAsync); + _ = nameof(CamelCaseClient.PatchSupermarketCashiersRelationshipAsync); + _ = nameof(CamelCaseClient.DeleteSupermarketCashiersRelationshipAsync); + _ = nameof(CamelCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(CamelCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(CamelCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(CamelCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(CamelCaseClient.PatchSupermarketStoreManagerRelationshipAsync); + + _ = nameof(SupermarketCollectionResponseDocument); + _ = nameof(LinksInResourceCollectionDocument); + _ = nameof(JsonapiObject); + _ = nameof(SupermarketDataInResponse); + _ = nameof(SupermarketResourceType.Supermarkets); + _ = nameof(SupermarketAttributesInResponse.NameOfCity); + _ = nameof(SupermarketRelationshipsInResponse.StoreManager); + _ = nameof(SupermarketRelationshipsInResponse.BackupStoreManager); + _ = nameof(LinksInResourceObject); + _ = nameof(SupermarketType); + _ = nameof(CamelCaseClient.GetSupermarketAsync); + _ = nameof(ToOneStaffMemberInResponse); + _ = nameof(NullableToOneStaffMemberInResponse); + _ = nameof(ToManyStaffMemberInResponse); + _ = nameof(LinksInRelationshipObject); + _ = nameof(StaffMemberIdentifier); + _ = nameof(StaffMemberResourceType.StaffMembers); + _ = nameof(SupermarketPrimaryResponseDocument); + _ = nameof(LinksInResourceDocument); + _ = nameof(StaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberDataInResponse); + _ = nameof(StaffMemberAttributesInResponse); + _ = nameof(NullableStaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberCollectionResponseDocument); + _ = nameof(StaffMemberIdentifierResponseDocument); + _ = nameof(LinksInResourceIdentifierDocument); + _ = nameof(NullableStaffMemberIdentifierResponseDocument); + _ = nameof(StaffMemberIdentifierCollectionResponseDocument); + _ = nameof(LinksInResourceIdentifierCollectionDocument); + _ = nameof(SupermarketPostRequestDocument); + _ = nameof(SupermarketDataInPostRequest); + _ = nameof(SupermarketAttributesInPostRequest); + _ = nameof(SupermarketRelationshipsInPostRequest); + _ = nameof(ToOneStaffMemberInRequest); + _ = nameof(NullableToOneStaffMemberInRequest); + _ = nameof(ToManyStaffMemberInRequest); + _ = nameof(SupermarketPatchRequestDocument); + _ = nameof(SupermarketDataInPatchRequest); + _ = nameof(SupermarketAttributesInPatchRequest); + _ = nameof(SupermarketRelationshipsInPatchRequest); + } +} diff --git a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json new file mode 100644 index 0000000000..2eaac9303c --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json @@ -0,0 +1,1466 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/supermarkets": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "supermarkets" + ], + "operationId": "postSupermarket", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patchSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarketPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "supermarkets" + ], + "operationId": "deleteSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/backupStoreManager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketBackupStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableStaffMemberSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketBackupStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableStaffMemberSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/backupStoreManager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableStaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableStaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patchSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableToOneStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/cashiers": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketCashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketCashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberCollectionResponseDocument" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/cashiers": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "supermarkets" + ], + "operationId": "postSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patchSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "supermarkets" + ], + "operationId": "deleteSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/storeManager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/storeManager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "getSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "headSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patchSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/toOneStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "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": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "nullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullableStaffMemberIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "nullableStaffMemberSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staffMemberDataInResponse" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "nullableToOneStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + } + }, + "additionalProperties": false + }, + "nullableToOneStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/nullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "staffMemberAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "staffMemberCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staffMemberDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "staffMemberDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/staffMemberResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/staffMemberAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "staffMemberIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/staffMemberResourceType" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "staffMemberIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staffMemberIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "staffMemberIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staffMemberIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "staffMemberResourceType": { + "enum": [ + "staffMembers" + ], + "type": "string" + }, + "staffMemberSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staffMemberDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "supermarketAttributesInPatchRequest": { + "type": "object", + "properties": { + "nameOfCity": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarketType" + } + }, + "additionalProperties": false + }, + "supermarketAttributesInPostRequest": { + "required": [ + "nameOfCity" + ], + "type": "object", + "properties": { + "nameOfCity": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarketType" + } + }, + "additionalProperties": false + }, + "supermarketAttributesInResponse": { + "type": "object", + "properties": { + "nameOfCity": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarketType" + } + }, + "additionalProperties": false + }, + "supermarketCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/supermarketDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "supermarketDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarketResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/supermarketAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/supermarketRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "supermarketDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarketResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/supermarketAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/supermarketRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "supermarketDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarketResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/supermarketAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/supermarketRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "supermarketPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarketDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "supermarketPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarketDataInPostRequest" + } + }, + "additionalProperties": false + }, + "supermarketPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarketDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/linksInResourceDocument" + } + }, + "additionalProperties": false + }, + "supermarketRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "storeManager": { + "$ref": "#/components/schemas/toOneStaffMemberInRequest" + }, + "backupStoreManager": { + "$ref": "#/components/schemas/nullableToOneStaffMemberInRequest" + }, + "cashiers": { + "$ref": "#/components/schemas/toManyStaffMemberInRequest" + } + }, + "additionalProperties": false + }, + "supermarketRelationshipsInPostRequest": { + "required": [ + "storeManager" + ], + "type": "object", + "properties": { + "storeManager": { + "$ref": "#/components/schemas/toOneStaffMemberInRequest" + }, + "backupStoreManager": { + "$ref": "#/components/schemas/nullableToOneStaffMemberInRequest" + }, + "cashiers": { + "$ref": "#/components/schemas/toManyStaffMemberInRequest" + } + }, + "additionalProperties": false + }, + "supermarketRelationshipsInResponse": { + "type": "object", + "properties": { + "storeManager": { + "$ref": "#/components/schemas/toOneStaffMemberInResponse" + }, + "backupStoreManager": { + "$ref": "#/components/schemas/nullableToOneStaffMemberInResponse" + }, + "cashiers": { + "$ref": "#/components/schemas/toManyStaffMemberInResponse" + } + }, + "additionalProperties": false + }, + "supermarketResourceType": { + "enum": [ + "supermarkets" + ], + "type": "string" + }, + "supermarketType": { + "enum": [ + "Traditional", + "Budget", + "Warehouse" + ], + "type": "string" + }, + "toManyStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staffMemberIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staffMemberIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "toOneStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staffMemberIdentifier" + } + }, + "additionalProperties": false + }, + "toOneStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staffMemberIdentifier" + }, + "links": { + "$ref": "#/components/schemas/linksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/NamingConventions/KebabCase/GeneratedTypesTests.cs b/test/OpenApiClientTests/NamingConventions/KebabCase/GeneratedTypesTests.cs new file mode 100644 index 0000000000..f4884561fd --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/KebabCase/GeneratedTypesTests.cs @@ -0,0 +1,77 @@ +using OpenApiClientTests.NamingConventions.KebabCase.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.NamingConventions.KebabCase; + +public sealed class GeneratedTypesTests +{ + [Fact] + public void Generated_code_is_named_as_expected() + { + _ = nameof(KebabCaseClient.GetSupermarketCollectionAsync); + _ = nameof(KebabCaseClient.GetSupermarketCollectionAsync); + _ = nameof(KebabCaseClient.PostSupermarketAsync); + _ = nameof(KebabCaseClient.GetSupermarketAsync); + _ = nameof(KebabCaseClient.GetSupermarketAsync); + _ = nameof(KebabCaseClient.PatchSupermarketAsync); + _ = nameof(KebabCaseClient.DeleteSupermarketAsync); + _ = nameof(KebabCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(KebabCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(KebabCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(KebabCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(KebabCaseClient.PatchSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(KebabCaseClient.GetSupermarketCashiersAsync); + _ = nameof(KebabCaseClient.GetSupermarketCashiersAsync); + _ = nameof(KebabCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(KebabCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(KebabCaseClient.PostSupermarketCashiersRelationshipAsync); + _ = nameof(KebabCaseClient.PatchSupermarketCashiersRelationshipAsync); + _ = nameof(KebabCaseClient.DeleteSupermarketCashiersRelationshipAsync); + _ = nameof(KebabCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(KebabCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(KebabCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(KebabCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(KebabCaseClient.PatchSupermarketStoreManagerRelationshipAsync); + + _ = nameof(SupermarketCollectionResponseDocument); + _ = nameof(LinksInResourceCollectionDocument); + _ = nameof(JsonapiObject); + _ = nameof(SupermarketDataInResponse); + _ = nameof(SupermarketResourceType.Supermarkets); + _ = nameof(SupermarketAttributesInResponse.NameOfCity); + _ = nameof(SupermarketRelationshipsInResponse.StoreManager); + _ = nameof(SupermarketRelationshipsInResponse.BackupStoreManager); + _ = nameof(LinksInResourceObject); + _ = nameof(SupermarketType); + _ = nameof(KebabCaseClient.GetSupermarketAsync); + _ = nameof(ToOneStaffMemberInResponse); + _ = nameof(NullableToOneStaffMemberInResponse); + _ = nameof(ToManyStaffMemberInResponse); + _ = nameof(LinksInRelationshipObject); + _ = nameof(StaffMemberIdentifier); + _ = nameof(StaffMemberResourceType.StaffMembers); + _ = nameof(SupermarketPrimaryResponseDocument); + _ = nameof(LinksInResourceDocument); + _ = nameof(StaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberDataInResponse); + _ = nameof(StaffMemberAttributesInResponse); + _ = nameof(NullableStaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberCollectionResponseDocument); + _ = nameof(StaffMemberIdentifierResponseDocument); + _ = nameof(LinksInResourceIdentifierDocument); + _ = nameof(NullableStaffMemberIdentifierResponseDocument); + _ = nameof(StaffMemberIdentifierCollectionResponseDocument); + _ = nameof(LinksInResourceIdentifierCollectionDocument); + _ = nameof(SupermarketPostRequestDocument); + _ = nameof(SupermarketDataInPostRequest); + _ = nameof(SupermarketAttributesInPostRequest); + _ = nameof(SupermarketRelationshipsInPostRequest); + _ = nameof(ToOneStaffMemberInRequest); + _ = nameof(NullableToOneStaffMemberInRequest); + _ = nameof(ToManyStaffMemberInRequest); + _ = nameof(SupermarketPatchRequestDocument); + _ = nameof(SupermarketDataInPatchRequest); + _ = nameof(SupermarketAttributesInPatchRequest); + _ = nameof(SupermarketRelationshipsInPatchRequest); + } +} diff --git a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json new file mode 100644 index 0000000000..1013e900a8 --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json @@ -0,0 +1,1466 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/supermarkets": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-collection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "supermarkets" + ], + "operationId": "post-supermarket", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-post-request-document" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-primary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-primary-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patch-supermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-patch-request-document" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/supermarket-primary-response-document" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "supermarkets" + ], + "operationId": "delete-supermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/backup-store-manager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-backup-store-manager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-staff-member-secondary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-backup-store-manager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-staff-member-secondary-response-document" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/backup-store-manager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-backup-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-staff-member-identifier-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-backup-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-staff-member-identifier-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patch-supermarket-backup-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullable-to-one-staff-member-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/cashiers": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-cashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-cashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-collection-response-document" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/cashiers": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-cashiers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-identifier-collection-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-cashiers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-identifier-collection-response-document" + } + } + } + } + } + }, + "post": { + "tags": [ + "supermarkets" + ], + "operationId": "post-supermarket-cashiers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-staff-member-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patch-supermarket-cashiers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-staff-member-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "supermarkets" + ], + "operationId": "delete-supermarket-cashiers-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-many-staff-member-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/supermarkets/{id}/store-manager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-store-manager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-secondary-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-store-manager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-secondary-response-document" + } + } + } + } + } + } + }, + "/supermarkets/{id}/relationships/store-manager": { + "get": { + "tags": [ + "supermarkets" + ], + "operationId": "get-supermarket-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-identifier-response-document" + } + } + } + } + } + }, + "head": { + "tags": [ + "supermarkets" + ], + "operationId": "head-supermarket-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/staff-member-identifier-response-document" + } + } + } + } + } + }, + "patch": { + "tags": [ + "supermarkets" + ], + "operationId": "patch-supermarket-store-manager-relationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/to-one-staff-member-in-request" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "components": { + "schemas": { + "jsonapi-object": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "links-in-relationship-object": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-collection-document": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-document": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-identifier-collection-document": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-identifier-document": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "links-in-resource-object": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "null-value": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "nullable-staff-member-identifier-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staff-member-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-document" + } + }, + "additionalProperties": false + }, + "nullable-staff-member-secondary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staff-member-data-in-response" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "nullable-to-one-staff-member-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staff-member-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + } + }, + "additionalProperties": false + }, + "nullable-to-one-staff-member-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/staff-member-identifier" + }, + { + "$ref": "#/components/schemas/null-value" + } + ] + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "staff-member-attributes-in-response": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "staff-member-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staff-member-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "staff-member-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/staff-member-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/staff-member-attributes-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "staff-member-identifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/staff-member-resource-type" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "staff-member-identifier-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staff-member-identifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-collection-document" + } + }, + "additionalProperties": false + }, + "staff-member-identifier-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staff-member-identifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-identifier-document" + } + }, + "additionalProperties": false + }, + "staff-member-resource-type": { + "enum": [ + "staff-members" + ], + "type": "string" + }, + "staff-member-secondary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staff-member-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "supermarket-attributes-in-patch-request": { + "type": "object", + "properties": { + "name-of-city": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarket-type" + } + }, + "additionalProperties": false + }, + "supermarket-attributes-in-post-request": { + "required": [ + "name-of-city" + ], + "type": "object", + "properties": { + "name-of-city": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarket-type" + } + }, + "additionalProperties": false + }, + "supermarket-attributes-in-response": { + "type": "object", + "properties": { + "name-of-city": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/supermarket-type" + } + }, + "additionalProperties": false + }, + "supermarket-collection-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/supermarket-data-in-response" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-collection-document" + } + }, + "additionalProperties": false + }, + "supermarket-data-in-patch-request": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarket-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/supermarket-attributes-in-patch-request" + }, + "relationships": { + "$ref": "#/components/schemas/supermarket-relationships-in-patch-request" + } + }, + "additionalProperties": false + }, + "supermarket-data-in-post-request": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarket-resource-type" + }, + "attributes": { + "$ref": "#/components/schemas/supermarket-attributes-in-post-request" + }, + "relationships": { + "$ref": "#/components/schemas/supermarket-relationships-in-post-request" + } + }, + "additionalProperties": false + }, + "supermarket-data-in-response": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/supermarket-resource-type" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/supermarket-attributes-in-response" + }, + "relationships": { + "$ref": "#/components/schemas/supermarket-relationships-in-response" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "supermarket-patch-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarket-data-in-patch-request" + } + }, + "additionalProperties": false + }, + "supermarket-post-request-document": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarket-data-in-post-request" + } + }, + "additionalProperties": false + }, + "supermarket-primary-response-document": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/supermarket-data-in-response" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/jsonapi-object" + }, + "links": { + "$ref": "#/components/schemas/links-in-resource-document" + } + }, + "additionalProperties": false + }, + "supermarket-relationships-in-patch-request": { + "type": "object", + "properties": { + "store-manager": { + "$ref": "#/components/schemas/to-one-staff-member-in-request" + }, + "backup-store-manager": { + "$ref": "#/components/schemas/nullable-to-one-staff-member-in-request" + }, + "cashiers": { + "$ref": "#/components/schemas/to-many-staff-member-in-request" + } + }, + "additionalProperties": false + }, + "supermarket-relationships-in-post-request": { + "required": [ + "store-manager" + ], + "type": "object", + "properties": { + "store-manager": { + "$ref": "#/components/schemas/to-one-staff-member-in-request" + }, + "backup-store-manager": { + "$ref": "#/components/schemas/nullable-to-one-staff-member-in-request" + }, + "cashiers": { + "$ref": "#/components/schemas/to-many-staff-member-in-request" + } + }, + "additionalProperties": false + }, + "supermarket-relationships-in-response": { + "type": "object", + "properties": { + "store-manager": { + "$ref": "#/components/schemas/to-one-staff-member-in-response" + }, + "backup-store-manager": { + "$ref": "#/components/schemas/nullable-to-one-staff-member-in-response" + }, + "cashiers": { + "$ref": "#/components/schemas/to-many-staff-member-in-response" + } + }, + "additionalProperties": false + }, + "supermarket-resource-type": { + "enum": [ + "supermarkets" + ], + "type": "string" + }, + "supermarket-type": { + "enum": [ + "Traditional", + "Budget", + "Warehouse" + ], + "type": "string" + }, + "to-many-staff-member-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staff-member-identifier" + } + } + }, + "additionalProperties": false + }, + "to-many-staff-member-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/staff-member-identifier" + } + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "to-one-staff-member-in-request": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staff-member-identifier" + } + }, + "additionalProperties": false + }, + "to-one-staff-member-in-response": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/staff-member-identifier" + }, + "links": { + "$ref": "#/components/schemas/links-in-relationship-object" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/NamingConventions/PascalCase/GeneratedTypesTests.cs b/test/OpenApiClientTests/NamingConventions/PascalCase/GeneratedTypesTests.cs new file mode 100644 index 0000000000..9ec0325e58 --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/PascalCase/GeneratedTypesTests.cs @@ -0,0 +1,77 @@ +using OpenApiClientTests.NamingConventions.PascalCase.GeneratedCode; +using Xunit; + +namespace OpenApiClientTests.NamingConventions.PascalCase; + +public sealed class GeneratedTypesTests +{ + [Fact] + public void Generated_code_is_named_as_expected() + { + _ = nameof(PascalCaseClient.GetSupermarketCollectionAsync); + _ = nameof(PascalCaseClient.GetSupermarketCollectionAsync); + _ = nameof(PascalCaseClient.PostSupermarketAsync); + _ = nameof(PascalCaseClient.GetSupermarketAsync); + _ = nameof(PascalCaseClient.GetSupermarketAsync); + _ = nameof(PascalCaseClient.PatchSupermarketAsync); + _ = nameof(PascalCaseClient.DeleteSupermarketAsync); + _ = nameof(PascalCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(PascalCaseClient.GetSupermarketBackupStoreManagerAsync); + _ = nameof(PascalCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(PascalCaseClient.GetSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(PascalCaseClient.PatchSupermarketBackupStoreManagerRelationshipAsync); + _ = nameof(PascalCaseClient.GetSupermarketCashiersAsync); + _ = nameof(PascalCaseClient.GetSupermarketCashiersAsync); + _ = nameof(PascalCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(PascalCaseClient.GetSupermarketCashiersRelationshipAsync); + _ = nameof(PascalCaseClient.PostSupermarketCashiersRelationshipAsync); + _ = nameof(PascalCaseClient.PatchSupermarketCashiersRelationshipAsync); + _ = nameof(PascalCaseClient.DeleteSupermarketCashiersRelationshipAsync); + _ = nameof(PascalCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(PascalCaseClient.GetSupermarketStoreManagerAsync); + _ = nameof(PascalCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(PascalCaseClient.GetSupermarketStoreManagerRelationshipAsync); + _ = nameof(PascalCaseClient.PatchSupermarketStoreManagerRelationshipAsync); + + _ = nameof(SupermarketCollectionResponseDocument); + _ = nameof(LinksInResourceCollectionDocument); + _ = nameof(JsonapiObject); + _ = nameof(SupermarketDataInResponse); + _ = nameof(SupermarketResourceType.Supermarkets); + _ = nameof(SupermarketAttributesInResponse.NameOfCity); + _ = nameof(SupermarketRelationshipsInResponse.StoreManager); + _ = nameof(SupermarketRelationshipsInResponse.BackupStoreManager); + _ = nameof(LinksInResourceObject); + _ = nameof(SupermarketType); + _ = nameof(PascalCaseClient.GetSupermarketAsync); + _ = nameof(ToOneStaffMemberInResponse); + _ = nameof(NullableToOneStaffMemberInResponse); + _ = nameof(ToManyStaffMemberInResponse); + _ = nameof(LinksInRelationshipObject); + _ = nameof(StaffMemberIdentifier); + _ = nameof(StaffMemberResourceType.StaffMembers); + _ = nameof(SupermarketPrimaryResponseDocument); + _ = nameof(LinksInResourceDocument); + _ = nameof(StaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberDataInResponse); + _ = nameof(StaffMemberAttributesInResponse); + _ = nameof(NullableStaffMemberSecondaryResponseDocument); + _ = nameof(StaffMemberCollectionResponseDocument); + _ = nameof(StaffMemberIdentifierResponseDocument); + _ = nameof(LinksInResourceIdentifierDocument); + _ = nameof(NullableStaffMemberIdentifierResponseDocument); + _ = nameof(StaffMemberIdentifierCollectionResponseDocument); + _ = nameof(LinksInResourceIdentifierCollectionDocument); + _ = nameof(SupermarketPostRequestDocument); + _ = nameof(SupermarketDataInPostRequest); + _ = nameof(SupermarketAttributesInPostRequest); + _ = nameof(SupermarketRelationshipsInPostRequest); + _ = nameof(ToOneStaffMemberInRequest); + _ = nameof(NullableToOneStaffMemberInRequest); + _ = nameof(ToManyStaffMemberInRequest); + _ = nameof(SupermarketPatchRequestDocument); + _ = nameof(SupermarketDataInPatchRequest); + _ = nameof(SupermarketAttributesInPatchRequest); + _ = nameof(SupermarketRelationshipsInPatchRequest); + } +} diff --git a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json new file mode 100644 index 0000000000..3906897de4 --- /dev/null +++ b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json @@ -0,0 +1,1466 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/Supermarkets": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketCollection", + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Supermarkets" + ], + "operationId": "PostSupermarket", + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPostRequestDocument" + } + } + } + }, + "responses": { + "201": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + } + }, + "/Supermarkets/{id}": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPrimaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPrimaryResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Supermarkets" + ], + "operationId": "PatchSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPatchRequestDocument" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/SupermarketPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "Supermarkets" + ], + "operationId": "DeleteSupermarket", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/Supermarkets/{id}/BackupStoreManager": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketBackupStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/NullableStaffMemberSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketBackupStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/NullableStaffMemberSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Supermarkets/{id}/relationships/BackupStoreManager": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/NullableStaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/NullableStaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Supermarkets" + ], + "operationId": "PatchSupermarketBackupStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/NullableToOneStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/Supermarkets/{id}/Cashiers": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketCashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketCashiers", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberCollectionResponseDocument" + } + } + } + } + } + } + }, + "/Supermarkets/{id}/relationships/Cashiers": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberIdentifierCollectionResponseDocument" + } + } + } + } + } + }, + "post": { + "tags": [ + "Supermarkets" + ], + "operationId": "PostSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ToManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "patch": { + "tags": [ + "Supermarkets" + ], + "operationId": "PatchSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ToManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + }, + "delete": { + "tags": [ + "Supermarkets" + ], + "operationId": "DeleteSupermarketCashiersRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ToManyStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + }, + "/Supermarkets/{id}/StoreManager": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberSecondaryResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketStoreManager", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberSecondaryResponseDocument" + } + } + } + } + } + } + }, + "/Supermarkets/{id}/relationships/StoreManager": { + "get": { + "tags": [ + "Supermarkets" + ], + "operationId": "GetSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "Supermarkets" + ], + "operationId": "HeadSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/StaffMemberIdentifierResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Supermarkets" + ], + "operationId": "PatchSupermarketStoreManagerRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ToOneStaffMemberInRequest" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + } + } + } + } + }, + "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": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinksInResourceCollectionDocument": { + "required": [ + "first", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinksInResourceIdentifierCollectionDocument": { + "required": [ + "first", + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + }, + "describedby": { + "type": "string" + }, + "related": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NullValue": { + "not": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + } + ], + "items": { } + }, + "nullable": true + }, + "NullableStaffMemberIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/NullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "NullableStaffMemberSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaffMemberDataInResponse" + }, + { + "$ref": "#/components/schemas/NullValue" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceDocument" + } + }, + "additionalProperties": false + }, + "NullableToOneStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/NullValue" + } + ] + } + }, + "additionalProperties": false + }, + "NullableToOneStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaffMemberIdentifier" + }, + { + "$ref": "#/components/schemas/NullValue" + } + ] + }, + "links": { + "$ref": "#/components/schemas/LinksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "StaffMemberAttributesInResponse": { + "type": "object", + "properties": { + "Name": { + "type": "string" + }, + "Age": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "StaffMemberCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffMemberDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "StaffMemberDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/StaffMemberResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/StaffMemberAttributesInResponse" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "StaffMemberIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/StaffMemberResourceType" + }, + "id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StaffMemberIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceIdentifierCollectionDocument" + } + }, + "additionalProperties": false + }, + "StaffMemberIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceIdentifierDocument" + } + }, + "additionalProperties": false + }, + "StaffMemberResourceType": { + "enum": [ + "StaffMembers" + ], + "type": "string" + }, + "StaffMemberSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/StaffMemberDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceDocument" + } + }, + "additionalProperties": false + }, + "SupermarketAttributesInPatchRequest": { + "type": "object", + "properties": { + "NameOfCity": { + "type": "string" + }, + "Kind": { + "$ref": "#/components/schemas/SupermarketType" + } + }, + "additionalProperties": false + }, + "SupermarketAttributesInPostRequest": { + "required": [ + "NameOfCity" + ], + "type": "object", + "properties": { + "NameOfCity": { + "type": "string" + }, + "Kind": { + "$ref": "#/components/schemas/SupermarketType" + } + }, + "additionalProperties": false + }, + "SupermarketAttributesInResponse": { + "type": "object", + "properties": { + "NameOfCity": { + "type": "string" + }, + "Kind": { + "$ref": "#/components/schemas/SupermarketType" + } + }, + "additionalProperties": false + }, + "SupermarketCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SupermarketDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceCollectionDocument" + } + }, + "additionalProperties": false + }, + "SupermarketDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/SupermarketResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/SupermarketAttributesInPatchRequest" + }, + "relationships": { + "$ref": "#/components/schemas/SupermarketRelationshipsInPatchRequest" + } + }, + "additionalProperties": false + }, + "SupermarketDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/SupermarketResourceType" + }, + "attributes": { + "$ref": "#/components/schemas/SupermarketAttributesInPostRequest" + }, + "relationships": { + "$ref": "#/components/schemas/SupermarketRelationshipsInPostRequest" + } + }, + "additionalProperties": false + }, + "SupermarketDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/SupermarketResourceType" + }, + "id": { + "type": "string" + }, + "attributes": { + "$ref": "#/components/schemas/SupermarketAttributesInResponse" + }, + "relationships": { + "$ref": "#/components/schemas/SupermarketRelationshipsInResponse" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "SupermarketPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SupermarketDataInPatchRequest" + } + }, + "additionalProperties": false + }, + "SupermarketPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SupermarketDataInPostRequest" + } + }, + "additionalProperties": false + }, + "SupermarketPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SupermarketDataInResponse" + }, + "meta": { + "type": "object", + "additionalProperties": { } + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonapiObject" + }, + "links": { + "$ref": "#/components/schemas/LinksInResourceDocument" + } + }, + "additionalProperties": false + }, + "SupermarketRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "StoreManager": { + "$ref": "#/components/schemas/ToOneStaffMemberInRequest" + }, + "BackupStoreManager": { + "$ref": "#/components/schemas/NullableToOneStaffMemberInRequest" + }, + "Cashiers": { + "$ref": "#/components/schemas/ToManyStaffMemberInRequest" + } + }, + "additionalProperties": false + }, + "SupermarketRelationshipsInPostRequest": { + "required": [ + "StoreManager" + ], + "type": "object", + "properties": { + "StoreManager": { + "$ref": "#/components/schemas/ToOneStaffMemberInRequest" + }, + "BackupStoreManager": { + "$ref": "#/components/schemas/NullableToOneStaffMemberInRequest" + }, + "Cashiers": { + "$ref": "#/components/schemas/ToManyStaffMemberInRequest" + } + }, + "additionalProperties": false + }, + "SupermarketRelationshipsInResponse": { + "type": "object", + "properties": { + "StoreManager": { + "$ref": "#/components/schemas/ToOneStaffMemberInResponse" + }, + "BackupStoreManager": { + "$ref": "#/components/schemas/NullableToOneStaffMemberInResponse" + }, + "Cashiers": { + "$ref": "#/components/schemas/ToManyStaffMemberInResponse" + } + }, + "additionalProperties": false + }, + "SupermarketResourceType": { + "enum": [ + "Supermarkets" + ], + "type": "string" + }, + "SupermarketType": { + "enum": [ + "Traditional", + "Budget", + "Warehouse" + ], + "type": "string" + }, + "ToManyStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + } + } + }, + "additionalProperties": false + }, + "ToManyStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + } + }, + "links": { + "$ref": "#/components/schemas/LinksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "ToOneStaffMemberInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + } + }, + "additionalProperties": false + }, + "ToOneStaffMemberInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/StaffMemberIdentifier" + }, + "links": { + "$ref": "#/components/schemas/LinksInRelationshipObject" + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/OpenApiClientTests.csproj b/test/OpenApiClientTests/OpenApiClientTests.csproj index 005d70d251..3f54c161b8 100644 --- a/test/OpenApiClientTests/OpenApiClientTests.csproj +++ b/test/OpenApiClientTests/OpenApiClientTests.csproj @@ -3,6 +3,12 @@ $(TargetFrameworkName) + + + PreserveNewest + + + @@ -28,22 +34,33 @@ - + OpenApiClientTests.LegacyClient.GeneratedCode OpenApiClient + OpenApiClient.cs NSwagCSharp - /UseBaseUrl:false /GenerateClientInterfaces:true /ClientClassAccessModifier:internal + /UseBaseUrl:false /GenerateClientInterfaces:true /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions + + + OpenApiClientTests.NamingConventions.KebabCase.GeneratedCode + KebabCaseClient + KebabCaseClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions + + + OpenApiClientTests.NamingConventions.CamelCase.GeneratedCode + CamelCaseClient + CamelCaseClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions + + + OpenApiClientTests.NamingConventions.PascalCase.GeneratedCode + PascalCaseClient + PascalCaseClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions - - - - - - - - - - - - + \ No newline at end of file diff --git a/test/OpenApiClientTests/xunit.runner.json b/test/OpenApiClientTests/xunit.runner.json new file mode 100644 index 0000000000..8f5f10571b --- /dev/null +++ b/test/OpenApiClientTests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/test/OpenApiTests/JsonElementExtensions.cs b/test/OpenApiTests/JsonElementExtensions.cs new file mode 100644 index 0000000000..d8dc7d4e20 --- /dev/null +++ b/test/OpenApiTests/JsonElementExtensions.cs @@ -0,0 +1,79 @@ +using System.Text.Json; +using BlushingPenguin.JsonPath; +using FluentAssertions; +using FluentAssertions.Execution; +using TestBuildingBlocks; + +namespace OpenApiTests; + +internal static class JsonElementExtensions +{ + public static JsonElementAssertions Should(this JsonElement source) + { + return new JsonElementAssertions(source); + } + + public static JsonElement ShouldContainPath(this JsonElement source, string path) + { + Func elementSelector = () => source.SelectToken(path, true)!.Value; + return elementSelector.Should().NotThrow().Subject; + } + + public static void ShouldBeString(this JsonElement source, string value) + { + source.ValueKind.Should().Be(JsonValueKind.String); + source.GetString().Should().Be(value); + } + + public static SchemaReferenceIdContainer ShouldBeSchemaReferenceId(this JsonElement source, string value) + { + source.ValueKind.Should().Be(JsonValueKind.String); + + string? jsonElementValue = source.GetString(); + jsonElementValue.ShouldNotBeNull(); + + string schemaReferenceId = jsonElementValue.Split('/').Last(); + schemaReferenceId.Should().Be(value); + + return new SchemaReferenceIdContainer(value); + } + + public sealed class SchemaReferenceIdContainer + { + public string SchemaReferenceId { get; } + + public SchemaReferenceIdContainer(string schemaReferenceId) + { + SchemaReferenceId = schemaReferenceId; + } + } + + public sealed class JsonElementAssertions : JsonElementAssertions + { + public JsonElementAssertions(JsonElement subject) + : base(subject) + { + } + } + + public class JsonElementAssertions + where TAssertions : JsonElementAssertions + { + private readonly JsonElement _subject; + + protected JsonElementAssertions(JsonElement subject) + { + _subject = subject; + } + + public void ContainProperty(string propertyName) + { + string json = _subject.ToString(); + + string escapedJson = json.Replace("{", "{{").Replace("}", "}}"); + + Execute.Assertion.ForCondition(_subject.TryGetProperty(propertyName, out _)) + .FailWith($"Expected JSON element '{escapedJson}' to contain a property named '{propertyName}'."); + } + } +} diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationTests.cs b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationTests.cs index 3d253e2dc9..a6da96f37b 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationTests.cs +++ b/test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationTests.cs @@ -1,53 +1,44 @@ using System.Reflection; +using System.Text.Json; using FluentAssertions; using TestBuildingBlocks; using Xunit; namespace OpenApiTests.LegacyOpenApiIntegration; -public sealed class LegacyOpenApiIntegrationTests - : IntegrationTestContext, LegacyIntegrationDbContext> +public sealed class LegacyOpenApiIntegrationTests : OpenApiTestContext, LegacyIntegrationDbContext> { public LegacyOpenApiIntegrationTests() { UseController(); UseController(); UseController(); + + SwaggerDocumentOutputPath = "test/OpenApiClientTests/LegacyClient"; } [Fact] - public async Task Retrieved_document_matches_expected_document() + public async Task Retrieved_swagger_document_matches_expected_document() { - // Arrange - const string embeddedResourceName = $"{nameof(OpenApiTests)}.{nameof(LegacyOpenApiIntegration)}.swagger.json"; - string expectedDocument = await LoadEmbeddedResourceAsync(embeddedResourceName); - const string requestUrl = "swagger/v1/swagger.json"; - // Act - string actualDocument = await GetAsync(requestUrl); + JsonElement jsonElement = await GetSwaggerDocumentAsync(); // Assert - actualDocument.Should().BeJson(expectedDocument); - } - - private async Task GetAsync(string requestUrl) - { - var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - using HttpClient client = Factory.CreateClient(); - HttpResponseMessage responseMessage = await client.SendAsync(request); - - return await responseMessage.Content.ReadAsStringAsync(); + string expectedJsonText = await GetExpectedSwaggerDocumentAsync(); + string actualJsonText = jsonElement.ToString(); + actualJsonText.Should().BeJson(expectedJsonText); } - private static async Task LoadEmbeddedResourceAsync(string name) + private static async Task GetExpectedSwaggerDocumentAsync() { + const string embeddedResourceName = $"{nameof(OpenApiTests)}.{nameof(LegacyOpenApiIntegration)}.swagger.json"; var assembly = Assembly.GetExecutingAssembly(); - await using Stream? stream = assembly.GetManifestResourceStream(name); + + await using Stream? stream = assembly.GetManifestResourceStream(embeddedResourceName); if (stream == null) { - throw new Exception($"Failed to load embedded resource '{name}'. Set Build Action to Embedded Resource in properties."); + throw new Exception($"Failed to load embedded resource '{embeddedResourceName}'. Set Build Action to Embedded Resource in properties."); } using var reader = new StreamReader(stream); diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json index ac62204a53..3fb80fb856 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json +++ b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json @@ -2043,7 +2043,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/airplanes-resource-type" + "$ref": "#/components/schemas/airplane-resource-type" }, "id": { "type": "string" @@ -2064,7 +2064,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/airplanes-resource-type" + "$ref": "#/components/schemas/airplane-resource-type" }, "attributes": { "$ref": "#/components/schemas/airplane-attributes-in-post-request" @@ -2084,7 +2084,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/airplanes-resource-type" + "$ref": "#/components/schemas/airplane-resource-type" }, "id": { "type": "string" @@ -2179,7 +2179,7 @@ }, "additionalProperties": false }, - "airplanes-resource-type": { + "airplane-resource-type": { "enum": [ "airplanes" ], @@ -2291,7 +2291,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flight-attendants-resource-type" + "$ref": "#/components/schemas/flight-attendant-resource-type" }, "id": { "type": "string" @@ -2312,7 +2312,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flight-attendants-resource-type" + "$ref": "#/components/schemas/flight-attendant-resource-type" }, "attributes": { "$ref": "#/components/schemas/flight-attendant-attributes-in-post-request" @@ -2332,7 +2332,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flight-attendants-resource-type" + "$ref": "#/components/schemas/flight-attendant-resource-type" }, "id": { "type": "string" @@ -2361,7 +2361,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flight-attendants-resource-type" + "$ref": "#/components/schemas/flight-attendant-resource-type" }, "id": { "type": "string" @@ -2501,6 +2501,12 @@ }, "additionalProperties": false }, + "flight-attendant-resource-type": { + "enum": [ + "flight-attendants" + ], + "type": "string" + }, "flight-attendant-secondary-response-document": { "required": [ "data", @@ -2524,12 +2530,6 @@ }, "additionalProperties": false }, - "flight-attendants-resource-type": { - "enum": [ - "flight-attendants" - ], - "type": "string" - }, "flight-attributes-in-patch-request": { "type": "object", "properties": { @@ -2616,7 +2616,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flights-resource-type" + "$ref": "#/components/schemas/flight-resource-type" }, "id": { "type": "string" @@ -2637,7 +2637,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flights-resource-type" + "$ref": "#/components/schemas/flight-resource-type" }, "relationships": { "$ref": "#/components/schemas/flight-relationships-in-post-request" @@ -2654,7 +2654,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flights-resource-type" + "$ref": "#/components/schemas/flight-resource-type" }, "id": { "type": "string" @@ -2683,7 +2683,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/flights-resource-type" + "$ref": "#/components/schemas/flight-resource-type" }, "id": { "type": "string" @@ -2821,7 +2821,7 @@ }, "additionalProperties": false }, - "flights-resource-type": { + "flight-resource-type": { "enum": [ "flights" ], @@ -3150,7 +3150,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/passengers-resource-type" + "$ref": "#/components/schemas/passenger-resource-type" }, "id": { "type": "string" @@ -3176,7 +3176,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/passengers-resource-type" + "$ref": "#/components/schemas/passenger-resource-type" }, "id": { "type": "string" @@ -3210,7 +3210,7 @@ }, "additionalProperties": false }, - "passengers-resource-type": { + "passenger-resource-type": { "enum": [ "passengers" ], @@ -3360,4 +3360,4 @@ } } } -} +} \ No newline at end of file diff --git a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs new file mode 100644 index 0000000000..242291c4f6 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseNamingConventionStartup.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.NamingConventions.CamelCase; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public sealed class CamelCaseNamingConventionStartup : OpenApiStartup + where TDbContext : DbContext +{ + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + options.SerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } +} diff --git a/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs new file mode 100644 index 0000000000..e0bbd5bad3 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/CamelCase/CamelCaseTests.cs @@ -0,0 +1,550 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.NamingConventions.CamelCase; + +public sealed class CamelCaseTests : IClassFixture, NamingConventionsDbContext>> +{ + private readonly OpenApiTestContext, NamingConventionsDbContext> _testContext; + + public CamelCaseTests(OpenApiTestContext, NamingConventionsDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/NamingConventions/CamelCase"; + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetCollection_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketCollection"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarketCollectionResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceCollectionDocumentSchemaRefId = null; + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapiObject"); + + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("linksInResourceCollectionDocument").SchemaReferenceId; + + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarketDataInResponse") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + + string? linksInResourceObjectSchemaRefId = null; + string? primaryResourceTypeSchemaRefId = null; + string? resourceAttributesInResponseSchemaRefId = null; + string? resourceRelationshipInResponseSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceObject") + .SchemaReferenceId; + + primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarketResourceType") + .SchemaReferenceId; + + resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + .ShouldBeSchemaReferenceId("supermarketAttributesInResponse").SchemaReferenceId; + + resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("supermarketRelationshipsInResponse").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + }); + + schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + { + enumValueElement.ShouldBeString("supermarkets"); + }); + + schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("nameOfCity"); + propertiesElement.Should().ContainProperty("kind"); + propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarketType"); + }); + + string? nullableToOneResourceResponseDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("storeManager"); + + propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInResponse"); + + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backupStoreManager.$ref") + .ShouldBeSchemaReferenceId("nullableToOneStaffMemberInResponse").SchemaReferenceId; + + propertiesElement.Should().ContainProperty("cashiers"); + propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInResponse"); + }); + + string? linksInRelationshipObjectSchemaRefId = null; + string? relatedResourceIdentifierSchemaRefId = null; + + schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + { + linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInRelationshipObject") + .SchemaReferenceId; + + relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + .ShouldBeSchemaReferenceId("staffMemberIdentifier").SchemaReferenceId; + + propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("nullValue"); + }); + + schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("related"); + }); + + string? relatedResourceTypeSchemaRefId = null; + + schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + { + relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staffMemberResourceType") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staffMembers"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSingle_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarketPrimaryResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("linksInResourceDocument") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_single_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/storeManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketStoreManager"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staffMemberSecondaryResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staffMemberDataInResponse") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staffMemberAttributesInResponse"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nullable_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/backupStoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketBackupStoreManager"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("nullableStaffMemberSecondaryResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_resources() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketCashiers"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staffMemberCollectionResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketStoreManagerRelationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staffMemberIdentifierResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("linksInResourceIdentifierDocument").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketBackupStoreManagerRelationship"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("nullableStaffMemberIdentifierResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("getSupermarketCashiersRelationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("linksInResourceIdentifierCollectionDocument").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Post_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("postSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarketPostRequestDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPostRequest") + .SchemaReferenceId; + }); + + string? resourceRelationshipInPostRequestSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPostRequest"); + + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("supermarketRelationshipsInPostRequest").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("storeManager"); + propertiesElement.ShouldContainPath("storeManager.$ref").ShouldBeSchemaReferenceId("toOneStaffMemberInRequest"); + + propertiesElement.Should().ContainProperty("backupStoreManager"); + propertiesElement.ShouldContainPath("backupStoreManager.$ref").ShouldBeSchemaReferenceId("nullableToOneStaffMemberInRequest"); + + propertiesElement.Should().ContainProperty("cashiers"); + propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("toManyStaffMemberInRequest"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("postSupermarketCashiersRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Patch_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patchSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarketPatchRequestDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarketDataInPatchRequest") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarketAttributesInPatchRequest"); + propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarketRelationshipsInPatchRequest"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/storeManager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patchSupermarketStoreManagerRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/backupStoreManager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patchSupermarketBackupStoreManagerRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patchSupermarketCashiersRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Delete_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("deleteSupermarket"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("deleteSupermarketCashiersRelationship"); + }); + }); + } +} diff --git a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs new file mode 100644 index 0000000000..ebd532aa65 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseNamingConventionStartup.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; +using OpenApiTests.LegacyOpenApiIntegration; + +namespace OpenApiTests.NamingConventions.KebabCase; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public sealed class KebabCaseNamingConventionStartup : OpenApiStartup + where TDbContext : DbContext +{ + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.SerializerOptions.PropertyNamingPolicy = JsonKebabCaseNamingPolicy.Instance; + options.SerializerOptions.DictionaryKeyPolicy = JsonKebabCaseNamingPolicy.Instance; + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } +} diff --git a/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs new file mode 100644 index 0000000000..0a532a6e3f --- /dev/null +++ b/test/OpenApiTests/NamingConventions/KebabCase/KebabCaseTests.cs @@ -0,0 +1,550 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.NamingConventions.KebabCase; + +public sealed class KebabCaseTests : IClassFixture, NamingConventionsDbContext>> +{ + private readonly OpenApiTestContext, NamingConventionsDbContext> _testContext; + + public KebabCaseTests(OpenApiTestContext, NamingConventionsDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/NamingConventions/KebabCase"; + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetCollection_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-collection"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarket-collection-response-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceCollectionDocumentSchemaRefId = null; + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("jsonapi-object"); + + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-resource-collection-document").SchemaReferenceId; + + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-response") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + + string? linksInResourceObjectSchemaRefId = null; + string? primaryResourceTypeSchemaRefId = null; + string? resourceAttributesInResponseSchemaRefId = null; + string? resourceRelationshipInResponseSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-object") + .SchemaReferenceId; + + primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("supermarket-resource-type") + .SchemaReferenceId; + + resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + .ShouldBeSchemaReferenceId("supermarket-attributes-in-response").SchemaReferenceId; + + resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("supermarket-relationships-in-response").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + }); + + schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + { + enumValueElement.ShouldBeString("supermarkets"); + }); + + schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("name-of-city"); + propertiesElement.Should().ContainProperty("kind"); + propertiesElement.ShouldContainPath("kind.$ref").ShouldBeSchemaReferenceId("supermarket-type"); + }); + + string? nullableToOneResourceResponseDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("store-manager"); + + propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-response"); + + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("backup-store-manager.$ref") + .ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-response").SchemaReferenceId; + + propertiesElement.Should().ContainProperty("cashiers"); + propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-response"); + }); + + string? linksInRelationshipObjectSchemaRefId = null; + string? relatedResourceIdentifierSchemaRefId = null; + + schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + { + linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-relationship-object").SchemaReferenceId; + + relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + .ShouldBeSchemaReferenceId("staff-member-identifier").SchemaReferenceId; + + propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("null-value"); + }); + + schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("related"); + }); + + string? relatedResourceTypeSchemaRefId = null; + + schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + { + relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("staff-member-resource-type") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("staff-members"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSingle_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarket-primary-response-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("links-in-resource-document") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_single_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/store-manager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-store-manager"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staff-member-secondary-response-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("staff-member-data-in-response") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("staff-member-attributes-in-response"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nullable_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/backup-store-manager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-backup-store-manager"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("nullable-staff-member-secondary-response-document"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_resources() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-cashiers"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staff-member-collection-response-document"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-store-manager-relationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staff-member-identifier-response-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-resource-identifier-document").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-backup-store-manager-relationship"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("nullable-staff-member-identifier-response-document"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("get-supermarket-cashiers-relationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("staff-member-identifier-collection-response-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("links-in-resource-identifier-collection-document").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Post_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("post-supermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarket-post-request-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-post-request") + .SchemaReferenceId; + }); + + string? resourceRelationshipInPostRequestSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-post-request"); + + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("supermarket-relationships-in-post-request").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("store-manager"); + propertiesElement.ShouldContainPath("store-manager.$ref").ShouldBeSchemaReferenceId("to-one-staff-member-in-request"); + + propertiesElement.Should().ContainProperty("backup-store-manager"); + propertiesElement.ShouldContainPath("backup-store-manager.$ref").ShouldBeSchemaReferenceId("nullable-to-one-staff-member-in-request"); + + propertiesElement.Should().ContainProperty("cashiers"); + propertiesElement.ShouldContainPath("cashiers.$ref").ShouldBeSchemaReferenceId("to-many-staff-member-in-request"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("post-supermarket-cashiers-relationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Patch_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./supermarkets/{id}.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patch-supermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("supermarket-patch-request-document").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("supermarket-data-in-patch-request") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("supermarket-attributes-in-patch-request"); + propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("supermarket-relationships-in-patch-request"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/store-manager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patch-supermarket-store-manager-relationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/backup-store-manager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patch-supermarket-backup-store-manager-relationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("patch-supermarket-cashiers-relationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Delete_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("delete-supermarket"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./supermarkets/{id}/relationships/cashiers.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("delete-supermarket-cashiers-relationship"); + }); + }); + } +} diff --git a/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs b/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs new file mode 100644 index 0000000000..eb2d0b52d2 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/NamingConventionsDbContext.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.NamingConventions; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class NamingConventionsDbContext : DbContext +{ + public DbSet Supermarkets => Set(); + + public NamingConventionsDbContext(DbContextOptions options) + : base(options) + { + } +} diff --git a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs new file mode 100644 index 0000000000..7c934b94e4 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseNamingConventionStartup.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; + +namespace OpenApiTests.NamingConventions.PascalCase; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public sealed class PascalCaseNamingConventionStartup : OpenApiStartup + where TDbContext : DbContext +{ + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.SerializerOptions.PropertyNamingPolicy = null; + options.SerializerOptions.DictionaryKeyPolicy = null; + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } +} diff --git a/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs new file mode 100644 index 0000000000..73868dcdad --- /dev/null +++ b/test/OpenApiTests/NamingConventions/PascalCase/PascalCaseTests.cs @@ -0,0 +1,551 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.NamingConventions.PascalCase; + +public sealed class PascalCaseTests + : IClassFixture, NamingConventionsDbContext>> +{ + private readonly OpenApiTestContext, NamingConventionsDbContext> _testContext; + + public PascalCaseTests(OpenApiTestContext, NamingConventionsDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputPath = "test/OpenApiClientTests/NamingConventions/PascalCase"; + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetCollection_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketCollection"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("SupermarketCollectionResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceCollectionDocumentSchemaRefId = null; + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("jsonapi.$ref").ShouldBeSchemaReferenceId("JsonapiObject"); + + linksInResourceCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("LinksInResourceCollectionDocument").SchemaReferenceId; + + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.items.$ref").ShouldBeSchemaReferenceId("SupermarketDataInResponse") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + + string? linksInResourceObjectSchemaRefId = null; + string? primaryResourceTypeSchemaRefId = null; + string? resourceAttributesInResponseSchemaRefId = null; + string? resourceRelationshipInResponseSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceObject") + .SchemaReferenceId; + + primaryResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("SupermarketResourceType") + .SchemaReferenceId; + + resourceAttributesInResponseSchemaRefId = propertiesElement.ShouldContainPath("attributes.$ref") + .ShouldBeSchemaReferenceId("SupermarketAttributesInResponse").SchemaReferenceId; + + resourceRelationshipInResponseSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("SupermarketRelationshipsInResponse").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + }); + + schemasElement.ShouldContainPath($"{primaryResourceTypeSchemaRefId}.enum[0]").With(enumValueElement => + { + enumValueElement.ShouldBeString("Supermarkets"); + }); + + schemasElement.ShouldContainPath($"{resourceAttributesInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("NameOfCity"); + propertiesElement.Should().ContainProperty("Kind"); + propertiesElement.ShouldContainPath("Kind.$ref").ShouldBeSchemaReferenceId("SupermarketType"); + }); + + string? nullableToOneResourceResponseDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceRelationshipInResponseSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("StoreManager"); + + propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInResponse"); + + nullableToOneResourceResponseDataSchemaRefId = propertiesElement.ShouldContainPath("BackupStoreManager.$ref") + .ShouldBeSchemaReferenceId("NullableToOneStaffMemberInResponse").SchemaReferenceId; + + propertiesElement.Should().ContainProperty("Cashiers"); + propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInResponse"); + }); + + string? linksInRelationshipObjectSchemaRefId = null; + string? relatedResourceIdentifierSchemaRefId = null; + + schemasElement.ShouldContainPath($"{nullableToOneResourceResponseDataSchemaRefId}.properties").With(propertiesElement => + { + linksInRelationshipObjectSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInRelationshipObject") + .SchemaReferenceId; + + relatedResourceIdentifierSchemaRefId = propertiesElement.ShouldContainPath("data.oneOf[0].$ref") + .ShouldBeSchemaReferenceId("StaffMemberIdentifier").SchemaReferenceId; + + propertiesElement.ShouldContainPath("data.oneOf[1].$ref").ShouldBeSchemaReferenceId("NullValue"); + }); + + schemasElement.ShouldContainPath($"{linksInRelationshipObjectSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("related"); + }); + + string? relatedResourceTypeSchemaRefId = null; + + schemasElement.ShouldContainPath($"{relatedResourceIdentifierSchemaRefId}.properties").With(propertiesElement => + { + relatedResourceTypeSchemaRefId = propertiesElement.ShouldContainPath("type.$ref").ShouldBeSchemaReferenceId("StaffMemberResourceType") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{relatedResourceTypeSchemaRefId}.enum[0]").ShouldBeSchemaReferenceId("StaffMembers"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSingle_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets/{id}.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("SupermarketPrimaryResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref").ShouldBeSchemaReferenceId("LinksInResourceDocument") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_single_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets/{id}/StoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketStoreManager"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("StaffMemberSecondaryResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("StaffMemberDataInResponse") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("StaffMemberAttributesInResponse"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_nullable_resource() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/BackupStoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketBackupStoreManager"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("NullableStaffMemberSecondaryResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetSecondary_endpoint_with_resources() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/Cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketCashiers"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("StaffMemberCollectionResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketStoreManagerRelationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("StaffMemberIdentifierResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("LinksInResourceIdentifierDocument").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketBackupStoreManagerRelationship"); + }); + + getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("NullableStaffMemberIdentifierResponseDocument"); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_GetRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.get").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("GetSupermarketCashiersRelationship"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("responses.200.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("StaffMemberIdentifierCollectionResponseDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? linksInResourceIdentifierCollectionDocumentSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + linksInResourceIdentifierCollectionDocumentSchemaRefId = propertiesElement.ShouldContainPath("links.$ref") + .ShouldBeSchemaReferenceId("LinksInResourceIdentifierCollectionDocument").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{linksInResourceIdentifierCollectionDocumentSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("self"); + propertiesElement.Should().ContainProperty("describedby"); + propertiesElement.Should().ContainProperty("related"); + propertiesElement.Should().ContainProperty("first"); + propertiesElement.Should().ContainProperty("last"); + propertiesElement.Should().ContainProperty("prev"); + propertiesElement.Should().ContainProperty("next"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Post_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PostSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("SupermarketPostRequestDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPostRequest") + .SchemaReferenceId; + }); + + string? resourceRelationshipInPostRequestSchemaRefId = null; + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPostRequest"); + + resourceRelationshipInPostRequestSchemaRefId = propertiesElement.ShouldContainPath("relationships.$ref") + .ShouldBeSchemaReferenceId("SupermarketRelationshipsInPostRequest").SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceRelationshipInPostRequestSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.Should().ContainProperty("StoreManager"); + propertiesElement.ShouldContainPath("StoreManager.$ref").ShouldBeSchemaReferenceId("ToOneStaffMemberInRequest"); + + propertiesElement.Should().ContainProperty("BackupStoreManager"); + propertiesElement.ShouldContainPath("BackupStoreManager.$ref").ShouldBeSchemaReferenceId("NullableToOneStaffMemberInRequest"); + + propertiesElement.Should().ContainProperty("Cashiers"); + propertiesElement.ShouldContainPath("Cashiers.$ref").ShouldBeSchemaReferenceId("ToManyStaffMemberInRequest"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PostRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.post").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PostSupermarketCashiersRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Patch_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + string? documentSchemaRefId = null; + + document.ShouldContainPath("paths./Supermarkets/{id}.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PatchSupermarket"); + }); + + documentSchemaRefId = getElement.ShouldContainPath("requestBody.content['application/vnd.api+json'].schema.$ref") + .ShouldBeSchemaReferenceId("SupermarketPatchRequestDocument").SchemaReferenceId; + }); + + document.ShouldContainPath("components.schemas").With(schemasElement => + { + string? resourceDataSchemaRefId = null; + + schemasElement.ShouldContainPath($"{documentSchemaRefId}.properties").With(propertiesElement => + { + resourceDataSchemaRefId = propertiesElement.ShouldContainPath("data.$ref").ShouldBeSchemaReferenceId("SupermarketDataInPatchRequest") + .SchemaReferenceId; + }); + + schemasElement.ShouldContainPath($"{resourceDataSchemaRefId}.properties").With(propertiesElement => + { + propertiesElement.ShouldContainPath("attributes.$ref").ShouldBeSchemaReferenceId("SupermarketAttributesInPatchRequest"); + propertiesElement.ShouldContainPath("relationships.$ref").ShouldBeSchemaReferenceId("SupermarketRelationshipsInPatchRequest"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/StoreManager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PatchSupermarketStoreManagerRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_nullable_ToOne_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/BackupStoreManager.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PatchSupermarketBackupStoreManagerRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_PatchRelationship_endpoint_with_ToMany_relationship() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.patch").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("PatchSupermarketCashiersRelationship"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_Delete_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("DeleteSupermarket"); + }); + }); + } + + [Fact] + public async Task Casing_convention_is_applied_to_DeleteRelationship_endpoint() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.ShouldContainPath("paths./Supermarkets/{id}/relationships/Cashiers.delete").With(getElement => + { + getElement.ShouldContainPath("operationId").With(operationElement => + { + operationElement.ShouldBeString("DeleteSupermarketCashiersRelationship"); + }); + }); + } +} diff --git a/test/OpenApiTests/NamingConventions/StaffMember.cs b/test/OpenApiTests/NamingConventions/StaffMember.cs new file mode 100644 index 0000000000..d14c30a422 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/StaffMember.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.NamingConventions; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class StaffMember : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [Attr] + public int Age { get; set; } +} diff --git a/test/OpenApiTests/NamingConventions/Supermarket.cs b/test/OpenApiTests/NamingConventions/Supermarket.cs new file mode 100644 index 0000000000..4bde8776da --- /dev/null +++ b/test/OpenApiTests/NamingConventions/Supermarket.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.NamingConventions; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.NamingConventions")] +public sealed class Supermarket : Identifiable +{ + [Attr] + public string NameOfCity { get; set; } = null!; + + [Attr] + public SupermarketType Kind { get; set; } + + [HasOne] + public StaffMember StoreManager { get; set; } = null!; + + [HasOne] + public StaffMember? BackupStoreManager { get; set; } + + [HasMany] + public ICollection Cashiers { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/NamingConventions/SupermarketType.cs b/test/OpenApiTests/NamingConventions/SupermarketType.cs new file mode 100644 index 0000000000..45570936f5 --- /dev/null +++ b/test/OpenApiTests/NamingConventions/SupermarketType.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; + +namespace OpenApiTests.NamingConventions; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public enum SupermarketType +{ + Traditional, + Budget, + Warehouse +} diff --git a/test/OpenApiTests/OpenApiTestContext.cs b/test/OpenApiTests/OpenApiTestContext.cs new file mode 100644 index 0000000000..a750929827 --- /dev/null +++ b/test/OpenApiTests/OpenApiTestContext.cs @@ -0,0 +1,80 @@ +using System.Reflection; +using System.Text.Json; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; +using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +namespace OpenApiTests; + +[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] +public class OpenApiTestContext : IntegrationTestContext + where TStartup : class + where TDbContext : DbContext +{ + private readonly Lazy> _lazySwaggerDocument; + + internal string? SwaggerDocumentOutputPath { private get; set; } + + public OpenApiTestContext() + { + _lazySwaggerDocument = new Lazy>(CreateSwaggerDocumentAsync, LazyThreadSafetyMode.ExecutionAndPublication); + } + + internal async Task GetSwaggerDocumentAsync() + { + return await _lazySwaggerDocument.Value; + } + + private async Task CreateSwaggerDocumentAsync() + { + string absoluteOutputPath = GetSwaggerDocumentAbsoluteOutputPath(SwaggerDocumentOutputPath); + + string content = await GetAsync("swagger/v1/swagger.json"); + + JsonElement rootElement = ParseSwaggerDocument(content); + await WriteToDiskAsync(absoluteOutputPath, rootElement); + + return rootElement; + } + + private static string GetSwaggerDocumentAbsoluteOutputPath(string? relativePath) + { + AssertHasSwaggerDocumentOutputPath(relativePath); + + string solutionRoot = Path.Combine(Assembly.GetExecutingAssembly().Location, "../../../../../../"); + string outputPath = Path.Combine(solutionRoot, relativePath, "swagger.g.json"); + + return Path.GetFullPath(outputPath); + } + + private static void AssertHasSwaggerDocumentOutputPath([SysNotNull] string? relativePath) + { + if (relativePath is null) + { + throw new Exception($"Property '{nameof(OpenApiTestContext)}.{nameof(SwaggerDocumentOutputPath)}' must be set."); + } + } + + private async Task GetAsync(string requestUrl) + { + using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); + + using HttpClient client = Factory.CreateClient(); + using HttpResponseMessage responseMessage = await client.SendAsync(request); + + return await responseMessage.Content.ReadAsStringAsync(); + } + + private static JsonElement ParseSwaggerDocument(string content) + { + using JsonDocument jsonDocument = JsonDocument.Parse(content); + return jsonDocument.RootElement.Clone(); + } + + private static async Task WriteToDiskAsync(string path, JsonElement jsonElement) + { + string contents = jsonElement.ToString(); + await File.WriteAllTextAsync(path, contents); + } +} diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj index e85a2fb288..f5a24feec9 100644 --- a/test/OpenApiTests/OpenApiTests.csproj +++ b/test/OpenApiTests/OpenApiTests.csproj @@ -4,8 +4,17 @@ - + + PreserveNewest + + + + + + + + + @@ -14,9 +23,6 @@ - - - - + diff --git a/test/OpenApiTests/xunit.runner.json b/test/OpenApiTests/xunit.runner.json new file mode 100644 index 0000000000..8f5f10571b --- /dev/null +++ b/test/OpenApiTests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/test/SourceGeneratorTests/SourceGeneratorTests.csproj b/test/SourceGeneratorTests/SourceGeneratorTests.csproj index fd0fb92262..4284854c44 100644 --- a/test/SourceGeneratorTests/SourceGeneratorTests.csproj +++ b/test/SourceGeneratorTests/SourceGeneratorTests.csproj @@ -1,4 +1,4 @@ - + $(TargetFrameworkName)