From 998befc03483d07fd170e23bc6085570ea4425c1 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 27 Jan 2022 14:53:39 -0500 Subject: [PATCH 1/7] - adds support for defining error responses as classes and not default responses Signed-off-by: Vincent Biret --- .../Common/Constants.cs | 10 ++++ .../Common/OpenApiOperationExtensions.cs | 51 +++++++++++++++++++ .../Generator/OpenApiErrorSchemaGenerator.cs | 12 ++--- .../Generator/OpenApiResponseGenerator.cs | 21 +++++++- .../OpenApiConvertSettings.cs | 6 +++ .../ComplexPropertyDeleteOperationHandler.cs | 7 +-- .../ComplexPropertyGetOperationHandler.cs | 2 +- .../ComplexPropertyPatchOperationHandler.cs | 7 +-- .../ComplexPropertyPostOperationHandler.cs | 7 +-- .../DollarCountGetOperationHandler.cs | 2 +- .../Operation/EntityDeleteOperationHandler.cs | 7 +-- .../Operation/EntityGetOperationHandler.cs | 2 +- .../Operation/EntityPatchOperationHandler.cs | 7 +-- .../Operation/EntitySetGetOperationHandler.cs | 2 +- .../EntitySetPostOperationHandler.cs | 2 +- .../MediaEntityGetOperationHandler.cs | 2 +- .../MediaEntityPutOperationHandler.cs | 7 +-- .../Operation/MetadataGetOperationHandler.cs | 2 +- ...avigationPropertyDeleteOperationHandler.cs | 7 +-- .../NavigationPropertyGetOperationHandler.cs | 2 +- ...NavigationPropertyPatchOperationHandler.cs | 7 +-- .../NavigationPropertyPostOperationHandler.cs | 2 +- .../ODataTypeCastGetOperationHandler.cs | 2 +- .../Operation/RefDeleteOperationHandler.cs | 7 +-- .../Operation/RefGetOperationHandler.cs | 2 +- .../Operation/RefPostOperationHandler.cs | 2 +- .../Operation/RefPutOperationHandler.cs | 7 +-- .../Operation/SingletonGetOperationHandler.cs | 2 +- .../SingletonPatchOperationHandler.cs | 7 +-- 29 files changed, 116 insertions(+), 87 deletions(-) create mode 100644 src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs diff --git a/src/Microsoft.OpenApi.OData.Reader/Common/Constants.cs b/src/Microsoft.OpenApi.OData.Reader/Common/Constants.cs index 570aff78..9288d1cf 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Common/Constants.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Common/Constants.cs @@ -45,6 +45,16 @@ internal static class Constants /// public static string StatusCodeDefault = "default"; + /// + /// Status code class: 4XX + /// + public static string StatusCodeClass4XX = "4XX"; + + /// + /// Status code class: 5XX + /// + public static string StatusCodeClass5XX = "5XX"; + /// /// Edm model error extension key. /// diff --git a/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs b/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs new file mode 100644 index 00000000..f494342b --- /dev/null +++ b/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs @@ -0,0 +1,51 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.OData.Generator; + +namespace Microsoft.OpenApi.OData.Common; + +/// +/// Extensions methods for the OpenApiOperation class. +/// +public static class OpenApiOperationExtensions +{ + /// + /// Adds a default response to the operation or 4XX/5XX responses for the errors depending on the settings. + /// Also adds a 204 no content response when requested. + /// + /// The operation. + /// The settings. + /// Whether to add a 204 no content response. + public static void AddErrorResponses(this OpenApiOperation operation, OpenApiConvertSettings settings, bool addNoContent = false) { + if (operation == null) { + throw Error.ArgumentNull(nameof(operation)); + } + if(settings == null) { + throw Error.ArgumentNull(nameof(settings)); + } + + if(operation.Responses == null) + { + operation.Responses = new(); + } + + if(addNoContent) + { + operation.Responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse()); + } + + if(settings.ErrorResponsesAsDefault) + { + operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + } + else + { + operation.Responses.Add(Constants.StatusCodeClass4XX, Constants.StatusCodeClass4XX.GetResponse()); + operation.Responses.Add(Constants.StatusCodeClass5XX, Constants.StatusCodeClass5XX.GetResponse()); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs index e8d93a66..c1a38876 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs @@ -86,13 +86,13 @@ public static OpenApiSchema CreateErrorMainSchema() Properties = new Dictionary { { - "code", new OpenApiSchema { Type = "string" } + "code", new OpenApiSchema { Type = "string", Nullable = false } }, { - "message", new OpenApiSchema { Type = "string" } + "message", new OpenApiSchema { Type = "string", Nullable = false, } }, { - "target", new OpenApiSchema { Type = "string" } + "target", new OpenApiSchema { Type = "string", Nullable = true } }, { "details", @@ -137,13 +137,13 @@ public static OpenApiSchema CreateErrorDetailSchema() Properties = new Dictionary { { - "code", new OpenApiSchema { Type = "string" } + "code", new OpenApiSchema { Type = "string", Nullable = false, } }, { - "message", new OpenApiSchema { Type = "string" } + "message", new OpenApiSchema { Type = "string", Nullable = false, } }, { - "target", new OpenApiSchema { Type = "string" } + "target", new OpenApiSchema { Type = "string", Nullable = true, } } } }; diff --git a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs index d68dc5b7..4d85996f 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs @@ -32,6 +32,24 @@ internal static class OpenApiResponseGenerator }, { Constants.StatusCode204, new OpenApiResponse { Description = "Success"} }, + { Constants.StatusCodeClass4XX, new OpenApiResponse + { + Reference = new OpenApiReference + { + Type = ReferenceType.Response, + Id = "error" + } + } + }, + { Constants.StatusCodeClass5XX, new OpenApiResponse + { + Reference = new OpenApiReference + { + Type = ReferenceType.Response, + Id = "error" + } + } + } }; /// @@ -41,8 +59,7 @@ internal static class OpenApiResponseGenerator /// The created . public static OpenApiResponse GetResponse(this string statusCode) { - OpenApiResponse response; - if (_responses.TryGetValue(statusCode, out response)) + if (_responses.TryGetValue(statusCode, out OpenApiResponse response)) { return response; } diff --git a/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs b/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs index 5770b484..fde609b6 100644 --- a/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs +++ b/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs @@ -215,6 +215,11 @@ public string PathPrefix /// public bool AddEnumDescriptionExtension { get; set; } = false; + /// + /// Gets/sets a value indicating whether the error responses should be described as a default response or as 4XX and 5XX error responses. + /// + public bool ErrorResponsesAsDefault { get; set; } = true; + internal OpenApiConvertSettings Clone() { var newSettings = new OpenApiConvertSettings @@ -251,6 +256,7 @@ internal OpenApiConvertSettings Clone() RequireDerivedTypesConstraintForODataTypeCastSegments = this.RequireDerivedTypesConstraintForODataTypeCastSegments, EnableDeprecationInformation = this.EnableDeprecationInformation, AddEnumDescriptionExtension = this.AddEnumDescriptionExtension, + ErrorResponsesAsDefault = this.ErrorResponsesAsDefault, }; return newSettings; diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyDeleteOperationHandler.cs index 6a1fa552..3b25b527 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyDeleteOperationHandler.cs @@ -55,12 +55,7 @@ protected override void SetParameters(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } protected override void SetSecurity(OpenApiOperation operation) diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs index 28a0b798..4d00f98a 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyGetOperationHandler.cs @@ -131,7 +131,7 @@ protected override void SetResponses(OpenApiOperation operation) SetCollectionResponse(operation); else SetSingleResponse(operation); - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPatchOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPatchOperationHandler.cs index 010e73b4..808a2a16 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPatchOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPatchOperationHandler.cs @@ -83,12 +83,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } protected override void SetSecurity(OpenApiOperation operation) diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs index e834194d..0416c4bb 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ComplexPropertyPostOperationHandler.cs @@ -96,12 +96,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs index c48851fa..c229358e 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/DollarCountGetOperationHandler.cs @@ -67,7 +67,7 @@ protected override void SetResponses(OpenApiOperation operation) } } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs index 79b638cc..53ff6d98 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityDeleteOperationHandler.cs @@ -62,12 +62,7 @@ protected override void SetParameters(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs index 3be5b2bb..59b3fb34 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs @@ -113,7 +113,7 @@ protected override void SetResponses(OpenApiOperation operation) } } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityPatchOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityPatchOperationHandler.cs index 463094f7..b4a7e157 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntityPatchOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntityPatchOperationHandler.cs @@ -86,12 +86,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs index 15d233de..fc3ea9b5 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetGetOperationHandler.cs @@ -145,7 +145,7 @@ protected override void SetResponses(OpenApiOperation operation) } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs index 381fd56b..22a266fc 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EntitySetPostOperationHandler.cs @@ -70,7 +70,7 @@ protected override void SetResponses(OpenApiOperation operation) } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs index 510baa94..86afaa06 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityGetOperationHandler.cs @@ -59,7 +59,7 @@ protected override void SetResponses(OpenApiOperation operation) } } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs index 6e1bfe69..d9138518 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MediaEntityPutOperationHandler.cs @@ -61,12 +61,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/MetadataGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/MetadataGetOperationHandler.cs index 0e20519d..6eaf138d 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/MetadataGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/MetadataGetOperationHandler.cs @@ -69,7 +69,7 @@ protected override void SetResponses(OpenApiOperation operation) } } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyDeleteOperationHandler.cs index e86b8e38..376aa1a3 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyDeleteOperationHandler.cs @@ -67,12 +67,7 @@ protected override void SetSecurity(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs index b11adaee..0656486a 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs @@ -138,7 +138,7 @@ protected override void SetResponses(OpenApiOperation operation) }; } - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPatchOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPatchOperationHandler.cs index 56ac7340..112eadb1 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPatchOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPatchOperationHandler.cs @@ -81,12 +81,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPostOperationHandler.cs index cca8c45e..03581b56 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyPostOperationHandler.cs @@ -120,7 +120,7 @@ protected override void SetResponses(OpenApiOperation operation) } } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs index 88f75b44..d25b720e 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs @@ -176,7 +176,7 @@ protected override void SetResponses(OpenApiOperation operation) else SetCollectionResponse(operation); - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/RefDeleteOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/RefDeleteOperationHandler.cs index ec68f984..a217a74b 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/RefDeleteOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/RefDeleteOperationHandler.cs @@ -81,12 +81,7 @@ protected override void SetSecurity(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs index 98a66ac6..65625ab0 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs @@ -122,7 +122,7 @@ protected override void SetResponses(OpenApiOperation operation) }; } - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/RefPostOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/RefPostOperationHandler.cs index ede32705..f2b08ba2 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/RefPostOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/RefPostOperationHandler.cs @@ -91,7 +91,7 @@ protected override void SetResponses(OpenApiOperation operation) } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/RefPutOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/RefPutOperationHandler.cs index 279b672b..ce41cf42 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/RefPutOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/RefPutOperationHandler.cs @@ -65,12 +65,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs index b81d714f..73364fbc 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs @@ -111,7 +111,7 @@ protected override void SetResponses(OpenApiOperation operation) } }; - operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); + operation.AddErrorResponses(Context.Settings, false); base.SetResponses(operation); } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs index 677308df..a1aadc84 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/SingletonPatchOperationHandler.cs @@ -83,12 +83,7 @@ protected override void SetRequestBody(OpenApiOperation operation) /// protected override void SetResponses(OpenApiOperation operation) { - operation.Responses = new OpenApiResponses - { - { Constants.StatusCode204, Constants.StatusCode204.GetResponse() }, - { Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() } - }; - + operation.AddErrorResponses(Context.Settings, true); base.SetResponses(operation); } From 5d6289213ed0c7dd91fd3e50ee686cbcccf3b293 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 27 Jan 2022 14:54:02 -0500 Subject: [PATCH 2/7] - updates test files for error classes Signed-off-by: Vincent Biret --- .../Resources/Basic.OpenApi.json | 6 ++++-- .../Resources/Basic.OpenApi.yaml | 2 ++ .../Resources/Empty.OpenApi.json | 6 ++++-- .../Resources/Empty.OpenApi.yaml | 2 ++ .../Resources/Multiple.Schema.OpenApi.json | 6 ++++-- .../Resources/Multiple.Schema.OpenApi.yaml | 2 ++ .../Resources/TripService.OpenApi.json | 6 ++++-- .../Resources/TripService.OpenApi.yaml | 2 ++ 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json index 3fee0719..338d58c5 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json @@ -2512,7 +2512,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true }, "details": { "type": "array", @@ -2540,7 +2541,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true } } }, diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml index df737205..ae4f1e4f 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml @@ -1668,6 +1668,7 @@ components: type: string target: type: string + nullable: true details: type: array items: @@ -1687,6 +1688,7 @@ components: type: string target: type: string + nullable: true ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json index 22cc046b..83a5d162 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json @@ -38,7 +38,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true }, "details": { "type": "array", @@ -66,7 +67,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true } } }, diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml index 9d53bdd0..ef8e7ce6 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml @@ -27,6 +27,7 @@ components: type: string target: type: string + nullable: true details: type: array items: @@ -46,6 +47,7 @@ components: type: string target: type: string + nullable: true ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json index 890b832b..ad9f8071 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json @@ -6569,7 +6569,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true }, "details": { "type": "array", @@ -6597,7 +6598,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true } } }, diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml index e8f30d87..9af30d3f 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml @@ -4771,6 +4771,7 @@ components: type: string target: type: string + nullable: true details: type: array items: @@ -4790,6 +4791,7 @@ components: type: string target: type: string + nullable: true ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json index 127be552..b210e627 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json @@ -32233,7 +32233,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true }, "details": { "type": "array", @@ -32261,7 +32262,8 @@ "type": "string" }, "target": { - "type": "string" + "type": "string", + "nullable": true } } }, diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml index 396afddf..b7450733 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml @@ -22454,6 +22454,7 @@ components: type: string target: type: string + nullable: true details: type: array items: @@ -22473,6 +22474,7 @@ components: type: string target: type: string + nullable: true ODataCountResponse: type: integer format: int32 From 15a39595f8ff9eb0c047be66eb5490bbcba8b930 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 27 Jan 2022 15:17:15 -0500 Subject: [PATCH 3/7] - adds unit test for error resposnses extension method Signed-off-by: Vincent Biret --- .../Common/OpenApiOperationExtensionsTests.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiOperationExtensionsTests.cs diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiOperationExtensionsTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiOperationExtensionsTests.cs new file mode 100644 index 00000000..576677d2 --- /dev/null +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiOperationExtensionsTests.cs @@ -0,0 +1,55 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; +using Xunit; +using Microsoft.OpenApi.OData.Common; + +namespace Microsoft.OpenApi.OData.Tests; + +public class OpenApiOperationExtensionsTests +{ + [Theory] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public void AddsErrorResponses(bool addNoContent, bool errorAsDefault) + { + // Arrange + var settings = new OpenApiConvertSettings { + ErrorResponsesAsDefault = errorAsDefault, + }; + var operation = new OpenApiOperation(); + + // Act + operation.AddErrorResponses(settings, addNoContent); + + // Assert + Assert.NotNull(operation.Responses); + Assert.Equal((errorAsDefault ? 1 : 2) + (addNoContent ? 1 : 0), operation.Responses.Count); + + if(addNoContent) + { + Assert.True(operation.Responses.ContainsKey("204")); + } + else + { + Assert.False(operation.Responses.ContainsKey("204")); + } + if(errorAsDefault) + { + Assert.True(operation.Responses.ContainsKey("default")); + Assert.False(operation.Responses.ContainsKey("4XX")); + Assert.False(operation.Responses.ContainsKey("5XX")); + } + else + { + Assert.False(operation.Responses.ContainsKey("default")); + Assert.True(operation.Responses.ContainsKey("4XX")); + Assert.True(operation.Responses.ContainsKey("5XX")); + } + } +} \ No newline at end of file From 28ffdcd433489f734563154dbba09e67a9a9accd Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 27 Jan 2022 15:26:02 -0500 Subject: [PATCH 4/7] - adds extension method to public apis Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt b/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt index 62ac6f8b..186f7b10 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt @@ -181,6 +181,8 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableDeprecationInformation.get Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableDeprecationInformation.set -> void Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumDescriptionExtension.get -> bool Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumDescriptionExtension.set -> void +Microsoft.OpenApi.OData.OpenApiConvertSettings.ErrorResponsesAsDefault.set -> void +Microsoft.OpenApi.OData.OpenApiConvertSettings.ErrorResponsesAsDefault.get -> bool override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.GetPathItemName(Microsoft.OpenApi.OData.OpenApiConvertSettings settings, System.Collections.Generic.HashSet parameters) -> string override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.Identifier.get -> string override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.EntityType.get -> Microsoft.OData.Edm.IEdmEntityType @@ -271,4 +273,6 @@ Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.RemovalDat Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.RemovalDate.set -> void Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.Date.get -> System.DateTime? Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.Date.set -> void -Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.Write(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void \ No newline at end of file +Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiDeprecationExtension.Write(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void +Microsoft.OpenApi.OData.Common.OpenApiOperationExtensions +static Microsoft.OpenApi.OData.Common.OpenApiOperationExtensions.AddErrorResponses(this Microsoft.OpenApi.Models.OpenApiOperation operation, Microsoft.OpenApi.OData.OpenApiConvertSettings settings, bool addNoContent = false) -> void From caa544f1276b1ee940fb6e74609fd77e2cd0bc64 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 28 Jan 2022 11:01:35 -0500 Subject: [PATCH 5/7] Update src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs Co-authored-by: Sam Xu --- .../Common/OpenApiOperationExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs b/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs index f494342b..9888e452 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Common/OpenApiOperationExtensions.cs @@ -20,7 +20,8 @@ public static class OpenApiOperationExtensions /// The operation. /// The settings. /// Whether to add a 204 no content response. - public static void AddErrorResponses(this OpenApiOperation operation, OpenApiConvertSettings settings, bool addNoContent = false) { + public static void AddErrorResponses(this OpenApiOperation operation, OpenApiConvertSettings settings, bool addNoContent = false) + { if (operation == null) { throw Error.ArgumentNull(nameof(operation)); } From 3dc6ddb71f20709a6fc8c5f434a5bdd04a1148fb Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 28 Jan 2022 14:02:53 -0500 Subject: [PATCH 6/7] - adds support for defining the inner error schema Signed-off-by: Vincent Biret --- .../Generator/OpenApiErrorSchemaGenerator.cs | 54 +++++++++++++++---- .../Generator/OpenApiSchemaGenerator.cs | 6 ++- .../OpenApiConvertSettings.cs | 6 +++ .../OpenApiErrorSchemaGeneraratorTests.cs | 46 ++++++++++++++++ .../Resources/Basic.OpenApi.V2.json | 7 ++- .../Resources/Basic.OpenApi.V2.yaml | 6 ++- .../Resources/Basic.OpenApi.json | 7 ++- .../Resources/Basic.OpenApi.yaml | 6 ++- .../Resources/Empty.OpenApi.V2.json | 7 ++- .../Resources/Empty.OpenApi.V2.yaml | 6 ++- .../Resources/Empty.OpenApi.json | 7 ++- .../Resources/Empty.OpenApi.yaml | 6 ++- .../Resources/Multiple.Schema.OpenApi.V2.json | 7 ++- .../Resources/Multiple.Schema.OpenApi.V2.yaml | 6 ++- .../Resources/Multiple.Schema.OpenApi.json | 7 ++- .../Resources/Multiple.Schema.OpenApi.yaml | 6 ++- .../Resources/TripService.OData.xml | 4 ++ .../Resources/TripService.OpenApi.V2.json | 17 +++++- .../Resources/TripService.OpenApi.V2.yaml | 13 ++++- .../Resources/TripService.OpenApi.json | 25 ++++++++- .../Resources/TripService.OpenApi.yaml | 19 ++++++- 21 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiErrorSchemaGeneraratorTests.cs diff --git a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs index c1a38876..a79e3f96 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs @@ -4,6 +4,8 @@ // ------------------------------------------------------------ using System.Collections.Generic; +using System.Linq; +using Microsoft.OData.Edm; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.OData.Common; using Microsoft.OpenApi.OData.Edm; @@ -26,18 +28,20 @@ public static IDictionary CreateODataErrorSchemas(this OD { Utils.CheckArgumentNull(context, nameof(context)); - IDictionary schemas = new Dictionary(); - - // odata.error - schemas.Add("odata.error", CreateErrorSchema()); + return new Dictionary() + { + // odata.error + { "odata.error", CreateErrorSchema() }, - // odata.error.main - schemas.Add("odata.error.main", CreateErrorMainSchema()); + // odata.error.main + { "odata.error.main", CreateErrorMainSchema() }, - // odata.error.detail - schemas.Add("odata.error.detail", CreateErrorDetailSchema()); + // odata.error.detail + { "odata.error.detail", CreateErrorDetailSchema() }, - return schemas; + // odata.error.innererror + { "odata.error.innererror", CreateInnerErrorSchema(context) } + }; } /// @@ -70,6 +74,31 @@ public static OpenApiSchema CreateErrorSchema() }; } + /// + /// Creates the inner error schema definition. If an "InnerError" complex type is defined in the root namespace, then this type will be used as the inner error type. + /// Otherwise, a default inner error type of object will be created. + /// + /// The OData to Open API context. + /// The inner error schema definition. + public static OpenApiSchema CreateInnerErrorSchema(ODataContext context) + { + Utils.CheckArgumentNull(context, nameof(context)); + + var rootNamespace = context.Model.DeclaredNamespaces.OrderBy(n => n.Count(x => x == '.')).FirstOrDefault(); + if(!string.IsNullOrEmpty(context.Settings.InnerErrorComplexTypeName) && + !string.IsNullOrEmpty(rootNamespace) && + context.Model.FindDeclaredType($"{rootNamespace}.{context.Settings.InnerErrorComplexTypeName}") is IEdmComplexType complexType) + { + return context.CreateSchemaTypeSchema(complexType); + } + + return new OpenApiSchema + { + Type = "object", + Description = "The structure of this object is service-specific" + }; + } + /// /// Create for "odata.error.main". /// @@ -113,8 +142,11 @@ public static OpenApiSchema CreateErrorMainSchema() "innererror", new OpenApiSchema { - Type = "object", - Description = "The structure of this object is service-specific" + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "odata.error.innererror" + } } } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs index e551c27d..217789b5 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs @@ -50,6 +50,10 @@ public static IDictionary CreateSchemas(this ODataContext case EdmSchemaElementKind.TypeDefinition: // Type definition { IEdmType reference = (IEdmType)element; + if(reference is IEdmComplexType && + reference.FullTypeName().EndsWith(context.Settings.InnerErrorComplexTypeName, StringComparison.Ordinal)) + continue; + schemas.Add(reference.FullTypeName(), context.CreateSchemaTypeSchema(reference)); } break; @@ -340,7 +344,7 @@ public static OpenApiSchema CreateSchemaTypeDefinitionSchema(this ODataContext c return context.CreateSchema(typeDefinition.UnderlyingType); } - private static OpenApiSchema CreateSchemaTypeSchema(this ODataContext context, IEdmType edmType) + internal static OpenApiSchema CreateSchemaTypeSchema(this ODataContext context, IEdmType edmType) { Debug.Assert(context != null); Debug.Assert(edmType != null); diff --git a/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs b/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs index fde609b6..7f3dcc3a 100644 --- a/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs +++ b/src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs @@ -220,6 +220,11 @@ public string PathPrefix /// public bool ErrorResponsesAsDefault { get; set; } = true; + /// + /// Gets/Sets the name of the complex type to look for in the main namespace to use as the inner error type. + /// + public string InnerErrorComplexTypeName { get; set; } = "InnerError"; + internal OpenApiConvertSettings Clone() { var newSettings = new OpenApiConvertSettings @@ -257,6 +262,7 @@ internal OpenApiConvertSettings Clone() EnableDeprecationInformation = this.EnableDeprecationInformation, AddEnumDescriptionExtension = this.AddEnumDescriptionExtension, ErrorResponsesAsDefault = this.ErrorResponsesAsDefault, + InnerErrorComplexTypeName = this.InnerErrorComplexTypeName }; return newSettings; diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiErrorSchemaGeneraratorTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiErrorSchemaGeneraratorTests.cs new file mode 100644 index 00000000..ed67c1a7 --- /dev/null +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiErrorSchemaGeneraratorTests.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OData.Edm; +using Microsoft.OpenApi.OData.Edm; +using Microsoft.OpenApi.OData.Generator; +using Xunit; + +namespace Microsoft.OpenApi.OData.Tests; +public class OpenApiErrorSchemaGeneraratorTests +{ + [Fact] + public void AddsEmptyInnerErrorWhenNoComplexTypeIsProvided() + { + IEdmModel model = EdmModelHelper.ContractServiceModel; + OpenApiConvertSettings settings = new() + { + ErrorResponsesAsDefault = false, + }; + ODataContext context = new(model, settings); + + var schema = OpenApiErrorSchemaGenerator.CreateInnerErrorSchema(context); + + Assert.Equal("object", schema.Type); + Assert.Empty(schema.Properties); + } + [Fact] + public void AddsInnerErrorPropertiesWhenComplexTypeIsProvided() + { + IEdmModel model = EdmModelHelper.TripServiceModel; + OpenApiConvertSettings settings = new() + { + ErrorResponsesAsDefault = false, + }; + ODataContext context = new(model, settings); + + var schema = OpenApiErrorSchemaGenerator.CreateInnerErrorSchema(context); + + Assert.Equal("object", schema.Type); + Assert.NotEmpty(schema.Properties); + Assert.Contains("Date", schema.Properties.Keys); + Assert.Contains("RequestId", schema.Properties.Keys); + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.json index 4faf1be7..393958e8 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.json @@ -2274,8 +2274,7 @@ } }, "innererror": { - "description": "The structure of this object is service-specific", - "type": "object" + "$ref": "#/definitions/odata.error.innererror" } } }, @@ -2297,6 +2296,10 @@ } } }, + "odata.error.innererror": { + "description": "The structure of this object is service-specific", + "type": "object" + }, "ODataCountResponse": { "format": "int32", "type": "integer" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.yaml index 01acdf95..a5a03553 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.yaml @@ -1511,8 +1511,7 @@ definitions: items: $ref: '#/definitions/odata.error.detail' innererror: - description: The structure of this object is service-specific - type: object + $ref: '#/definitions/odata.error.innererror' odata.error.detail: required: - code @@ -1525,6 +1524,9 @@ definitions: type: string target: type: string + odata.error.innererror: + description: The structure of this object is service-specific + type: object ODataCountResponse: format: int32 type: integer diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json index 338d58c5..0133ff42 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json @@ -2522,8 +2522,7 @@ } }, "innererror": { - "type": "object", - "description": "The structure of this object is service-specific" + "$ref": "#/components/schemas/odata.error.innererror" } } }, @@ -2546,6 +2545,10 @@ } } }, + "odata.error.innererror": { + "type": "object", + "description": "The structure of this object is service-specific" + }, "ODataCountResponse": { "type": "integer", "format": "int32" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml index ae4f1e4f..6f52f937 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml @@ -1674,8 +1674,7 @@ components: items: $ref: '#/components/schemas/odata.error.detail' innererror: - type: object - description: The structure of this object is service-specific + $ref: '#/components/schemas/odata.error.innererror' odata.error.detail: required: - code @@ -1689,6 +1688,9 @@ components: target: type: string nullable: true + odata.error.innererror: + type: object + description: The structure of this object is service-specific ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.json index 14425f18..37f8e330 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.json @@ -45,8 +45,7 @@ } }, "innererror": { - "description": "The structure of this object is service-specific", - "type": "object" + "$ref": "#/definitions/odata.error.innererror" } } }, @@ -68,6 +67,10 @@ } } }, + "odata.error.innererror": { + "description": "The structure of this object is service-specific", + "type": "object" + }, "ODataCountResponse": { "format": "int32", "type": "integer" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.yaml index efa5bec8..30b316e6 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.yaml @@ -32,8 +32,7 @@ definitions: items: $ref: '#/definitions/odata.error.detail' innererror: - description: The structure of this object is service-specific - type: object + $ref: '#/definitions/odata.error.innererror' odata.error.detail: required: - code @@ -46,6 +45,9 @@ definitions: type: string target: type: string + odata.error.innererror: + description: The structure of this object is service-specific + type: object ODataCountResponse: format: int32 type: integer diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json index 83a5d162..095be6b5 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json @@ -48,8 +48,7 @@ } }, "innererror": { - "type": "object", - "description": "The structure of this object is service-specific" + "$ref": "#/components/schemas/odata.error.innererror" } } }, @@ -72,6 +71,10 @@ } } }, + "odata.error.innererror": { + "type": "object", + "description": "The structure of this object is service-specific" + }, "ODataCountResponse": { "type": "integer", "format": "int32" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml index ef8e7ce6..2aab48d6 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml @@ -33,8 +33,7 @@ components: items: $ref: '#/components/schemas/odata.error.detail' innererror: - type: object - description: The structure of this object is service-specific + $ref: '#/components/schemas/odata.error.innererror' odata.error.detail: required: - code @@ -48,6 +47,9 @@ components: target: type: string nullable: true + odata.error.innererror: + type: object + description: The structure of this object is service-specific ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.json index f66c7d63..a7c5062c 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.json @@ -5849,8 +5849,7 @@ } }, "innererror": { - "description": "The structure of this object is service-specific", - "type": "object" + "$ref": "#/definitions/odata.error.innererror" } } }, @@ -5872,6 +5871,10 @@ } } }, + "odata.error.innererror": { + "description": "The structure of this object is service-specific", + "type": "object" + }, "ODataCountResponse": { "format": "int32", "type": "integer" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.yaml index 4e0dd01d..2c68f0ed 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.yaml @@ -4291,8 +4291,7 @@ definitions: items: $ref: '#/definitions/odata.error.detail' innererror: - description: The structure of this object is service-specific - type: object + $ref: '#/definitions/odata.error.innererror' odata.error.detail: required: - code @@ -4305,6 +4304,9 @@ definitions: type: string target: type: string + odata.error.innererror: + description: The structure of this object is service-specific + type: object ODataCountResponse: format: int32 type: integer diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json index ad9f8071..df40ed0c 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json @@ -6579,8 +6579,7 @@ } }, "innererror": { - "type": "object", - "description": "The structure of this object is service-specific" + "$ref": "#/components/schemas/odata.error.innererror" } } }, @@ -6603,6 +6602,10 @@ } } }, + "odata.error.innererror": { + "type": "object", + "description": "The structure of this object is service-specific" + }, "ODataCountResponse": { "type": "integer", "format": "int32" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml index 9af30d3f..33254da9 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.yaml @@ -4777,8 +4777,7 @@ components: items: $ref: '#/components/schemas/odata.error.detail' innererror: - type: object - description: The structure of this object is service-specific + $ref: '#/components/schemas/odata.error.innererror' odata.error.detail: required: - code @@ -4792,6 +4791,9 @@ components: target: type: string nullable: true + odata.error.innererror: + type: object + description: The structure of this object is service-specific ODataCountResponse: type: integer format: int32 diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OData.xml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OData.xml index ab636673..4e10c635 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OData.xml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OData.xml @@ -2,6 +2,10 @@ + + + + diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.json index fce7d605..fccfe902 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.json @@ -28773,8 +28773,7 @@ } }, "innererror": { - "description": "The structure of this object is service-specific", - "type": "object" + "$ref": "#/definitions/odata.error.innererror" } } }, @@ -28796,6 +28795,20 @@ } } }, + "odata.error.innererror": { + "title": "InnerError", + "type": "object", + "properties": { + "Date": { + "format": "date-time", + "pattern": "^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$", + "type": "string" + }, + "RequestId": { + "type": "string" + } + } + }, "ODataCountResponse": { "format": "int32", "type": "integer" diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.yaml index 0bdaee62..bc1c5648 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.V2.yaml @@ -20376,8 +20376,7 @@ definitions: items: $ref: '#/definitions/odata.error.detail' innererror: - description: The structure of this object is service-specific - type: object + $ref: '#/definitions/odata.error.innererror' odata.error.detail: required: - code @@ -20390,6 +20389,16 @@ definitions: type: string target: type: string + odata.error.innererror: + title: InnerError + type: object + properties: + Date: + format: date-time + pattern: '^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$' + type: string + RequestId: + type: string ODataCountResponse: format: int32 type: integer diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json index b210e627..affb4044 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.json @@ -32243,8 +32243,7 @@ } }, "innererror": { - "type": "object", - "description": "The structure of this object is service-specific" + "$ref": "#/components/schemas/odata.error.innererror" } } }, @@ -32267,6 +32266,22 @@ } } }, + "odata.error.innererror": { + "title": "InnerError", + "type": "object", + "properties": { + "Date": { + "pattern": "^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$", + "type": "string", + "format": "date-time", + "nullable": true + }, + "RequestId": { + "type": "string", + "nullable": true + } + } + }, "ODataCountResponse": { "type": "integer", "format": "int32" @@ -32616,6 +32631,12 @@ } }, "examples": { + "Microsoft.OData.Service.Sample.TrippinInMemory.Models.InnerError": { + "value": { + "Date": "0001-01-01T00:00:00.0000000+00:00", + "RequestId": "String" + } + }, "Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person": { "value": { "AddressInfo": [ diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml index b7450733..81e79dfe 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/TripService.OpenApi.yaml @@ -22460,8 +22460,7 @@ components: items: $ref: '#/components/schemas/odata.error.detail' innererror: - type: object - description: The structure of this object is service-specific + $ref: '#/components/schemas/odata.error.innererror' odata.error.detail: required: - code @@ -22475,6 +22474,18 @@ components: target: type: string nullable: true + odata.error.innererror: + title: InnerError + type: object + properties: + Date: + pattern: '^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$' + type: string + format: date-time + nullable: true + RequestId: + type: string + nullable: true ODataCountResponse: type: integer format: int32 @@ -22702,6 +22713,10 @@ components: schema: type: string examples: + Microsoft.OData.Service.Sample.TrippinInMemory.Models.InnerError: + value: + Date: '0001-01-01T00:00:00.0000000+00:00' + RequestId: String Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person: value: AddressInfo: From b1dba8020ebf06dfe5b7d50729ed312f08d88ae3 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 28 Jan 2022 14:05:15 -0500 Subject: [PATCH 7/7] - adds inner error complex type name to public api Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt b/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt index 186f7b10..a721f3b3 100644 --- a/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt @@ -183,6 +183,8 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumDescriptionExtension.get - Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumDescriptionExtension.set -> void Microsoft.OpenApi.OData.OpenApiConvertSettings.ErrorResponsesAsDefault.set -> void Microsoft.OpenApi.OData.OpenApiConvertSettings.ErrorResponsesAsDefault.get -> bool +Microsoft.OpenApi.OData.OpenApiConvertSettings.InnerErrorComplexTypeName.set -> void +Microsoft.OpenApi.OData.OpenApiConvertSettings.InnerErrorComplexTypeName.get -> string override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.GetPathItemName(Microsoft.OpenApi.OData.OpenApiConvertSettings settings, System.Collections.Generic.HashSet parameters) -> string override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.Identifier.get -> string override Microsoft.OpenApi.OData.Edm.ODataDollarCountSegment.EntityType.get -> Microsoft.OData.Edm.IEdmEntityType