Skip to content

Commit 3dc6ddb

Browse files
committed
- adds support for defining the inner error schema
Signed-off-by: Vincent Biret <vibiret@microsoft.com>
1 parent caa544f commit 3dc6ddb

21 files changed

+224
-44
lines changed

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiErrorSchemaGenerator.cs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// ------------------------------------------------------------
55

66
using System.Collections.Generic;
7+
using System.Linq;
8+
using Microsoft.OData.Edm;
79
using Microsoft.OpenApi.Models;
810
using Microsoft.OpenApi.OData.Common;
911
using Microsoft.OpenApi.OData.Edm;
@@ -26,18 +28,20 @@ public static IDictionary<string, OpenApiSchema> CreateODataErrorSchemas(this OD
2628
{
2729
Utils.CheckArgumentNull(context, nameof(context));
2830

29-
IDictionary<string, OpenApiSchema> schemas = new Dictionary<string, OpenApiSchema>();
30-
31-
// odata.error
32-
schemas.Add("odata.error", CreateErrorSchema());
31+
return new Dictionary<string, OpenApiSchema>()
32+
{
33+
// odata.error
34+
{ "odata.error", CreateErrorSchema() },
3335

34-
// odata.error.main
35-
schemas.Add("odata.error.main", CreateErrorMainSchema());
36+
// odata.error.main
37+
{ "odata.error.main", CreateErrorMainSchema() },
3638

37-
// odata.error.detail
38-
schemas.Add("odata.error.detail", CreateErrorDetailSchema());
39+
// odata.error.detail
40+
{ "odata.error.detail", CreateErrorDetailSchema() },
3941

40-
return schemas;
42+
// odata.error.innererror
43+
{ "odata.error.innererror", CreateInnerErrorSchema(context) }
44+
};
4145
}
4246

4347
/// <summary>
@@ -70,6 +74,31 @@ public static OpenApiSchema CreateErrorSchema()
7074
};
7175
}
7276

77+
/// <summary>
78+
/// 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.
79+
/// Otherwise, a default inner error type of object will be created.
80+
/// </summary>
81+
/// <param name="context">The OData to Open API context.</param>
82+
/// <returns>The inner error schema definition.</returns>
83+
public static OpenApiSchema CreateInnerErrorSchema(ODataContext context)
84+
{
85+
Utils.CheckArgumentNull(context, nameof(context));
86+
87+
var rootNamespace = context.Model.DeclaredNamespaces.OrderBy(n => n.Count(x => x == '.')).FirstOrDefault();
88+
if(!string.IsNullOrEmpty(context.Settings.InnerErrorComplexTypeName) &&
89+
!string.IsNullOrEmpty(rootNamespace) &&
90+
context.Model.FindDeclaredType($"{rootNamespace}.{context.Settings.InnerErrorComplexTypeName}") is IEdmComplexType complexType)
91+
{
92+
return context.CreateSchemaTypeSchema(complexType);
93+
}
94+
95+
return new OpenApiSchema
96+
{
97+
Type = "object",
98+
Description = "The structure of this object is service-specific"
99+
};
100+
}
101+
73102
/// <summary>
74103
/// Create <see cref="OpenApiSchema"/> for "odata.error.main".
75104
/// </summary>
@@ -113,8 +142,11 @@ public static OpenApiSchema CreateErrorMainSchema()
113142
"innererror",
114143
new OpenApiSchema
115144
{
116-
Type = "object",
117-
Description = "The structure of this object is service-specific"
145+
Reference = new OpenApiReference
146+
{
147+
Type = ReferenceType.Schema,
148+
Id = "odata.error.innererror"
149+
}
118150
}
119151
}
120152
}

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiSchemaGenerator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public static IDictionary<string, OpenApiSchema> CreateSchemas(this ODataContext
5050
case EdmSchemaElementKind.TypeDefinition: // Type definition
5151
{
5252
IEdmType reference = (IEdmType)element;
53+
if(reference is IEdmComplexType &&
54+
reference.FullTypeName().EndsWith(context.Settings.InnerErrorComplexTypeName, StringComparison.Ordinal))
55+
continue;
56+
5357
schemas.Add(reference.FullTypeName(), context.CreateSchemaTypeSchema(reference));
5458
}
5559
break;
@@ -340,7 +344,7 @@ public static OpenApiSchema CreateSchemaTypeDefinitionSchema(this ODataContext c
340344
return context.CreateSchema(typeDefinition.UnderlyingType);
341345
}
342346

343-
private static OpenApiSchema CreateSchemaTypeSchema(this ODataContext context, IEdmType edmType)
347+
internal static OpenApiSchema CreateSchemaTypeSchema(this ODataContext context, IEdmType edmType)
344348
{
345349
Debug.Assert(context != null);
346350
Debug.Assert(edmType != null);

src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ public string PathPrefix
220220
/// </summary>
221221
public bool ErrorResponsesAsDefault { get; set; } = true;
222222

223+
/// <summary>
224+
/// Gets/Sets the name of the complex type to look for in the main namespace to use as the inner error type.
225+
/// </summary>
226+
public string InnerErrorComplexTypeName { get; set; } = "InnerError";
227+
223228
internal OpenApiConvertSettings Clone()
224229
{
225230
var newSettings = new OpenApiConvertSettings
@@ -257,6 +262,7 @@ internal OpenApiConvertSettings Clone()
257262
EnableDeprecationInformation = this.EnableDeprecationInformation,
258263
AddEnumDescriptionExtension = this.AddEnumDescriptionExtension,
259264
ErrorResponsesAsDefault = this.ErrorResponsesAsDefault,
265+
InnerErrorComplexTypeName = this.InnerErrorComplexTypeName
260266
};
261267

262268
return newSettings;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// ------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4+
// ------------------------------------------------------------
5+
6+
using Microsoft.OData.Edm;
7+
using Microsoft.OpenApi.OData.Edm;
8+
using Microsoft.OpenApi.OData.Generator;
9+
using Xunit;
10+
11+
namespace Microsoft.OpenApi.OData.Tests;
12+
public class OpenApiErrorSchemaGeneraratorTests
13+
{
14+
[Fact]
15+
public void AddsEmptyInnerErrorWhenNoComplexTypeIsProvided()
16+
{
17+
IEdmModel model = EdmModelHelper.ContractServiceModel;
18+
OpenApiConvertSettings settings = new()
19+
{
20+
ErrorResponsesAsDefault = false,
21+
};
22+
ODataContext context = new(model, settings);
23+
24+
var schema = OpenApiErrorSchemaGenerator.CreateInnerErrorSchema(context);
25+
26+
Assert.Equal("object", schema.Type);
27+
Assert.Empty(schema.Properties);
28+
}
29+
[Fact]
30+
public void AddsInnerErrorPropertiesWhenComplexTypeIsProvided()
31+
{
32+
IEdmModel model = EdmModelHelper.TripServiceModel;
33+
OpenApiConvertSettings settings = new()
34+
{
35+
ErrorResponsesAsDefault = false,
36+
};
37+
ODataContext context = new(model, settings);
38+
39+
var schema = OpenApiErrorSchemaGenerator.CreateInnerErrorSchema(context);
40+
41+
Assert.Equal("object", schema.Type);
42+
Assert.NotEmpty(schema.Properties);
43+
Assert.Contains("Date", schema.Properties.Keys);
44+
Assert.Contains("RequestId", schema.Properties.Keys);
45+
}
46+
}

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2274,8 +2274,7 @@
22742274
}
22752275
},
22762276
"innererror": {
2277-
"description": "The structure of this object is service-specific",
2278-
"type": "object"
2277+
"$ref": "#/definitions/odata.error.innererror"
22792278
}
22802279
}
22812280
},
@@ -2297,6 +2296,10 @@
22972296
}
22982297
}
22992298
},
2299+
"odata.error.innererror": {
2300+
"description": "The structure of this object is service-specific",
2301+
"type": "object"
2302+
},
23002303
"ODataCountResponse": {
23012304
"format": "int32",
23022305
"type": "integer"

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.V2.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,8 +1511,7 @@ definitions:
15111511
items:
15121512
$ref: '#/definitions/odata.error.detail'
15131513
innererror:
1514-
description: The structure of this object is service-specific
1515-
type: object
1514+
$ref: '#/definitions/odata.error.innererror'
15161515
odata.error.detail:
15171516
required:
15181517
- code
@@ -1525,6 +1524,9 @@ definitions:
15251524
type: string
15261525
target:
15271526
type: string
1527+
odata.error.innererror:
1528+
description: The structure of this object is service-specific
1529+
type: object
15281530
ODataCountResponse:
15291531
format: int32
15301532
type: integer

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,8 +2522,7 @@
25222522
}
25232523
},
25242524
"innererror": {
2525-
"type": "object",
2526-
"description": "The structure of this object is service-specific"
2525+
"$ref": "#/components/schemas/odata.error.innererror"
25272526
}
25282527
}
25292528
},
@@ -2546,6 +2545,10 @@
25462545
}
25472546
}
25482547
},
2548+
"odata.error.innererror": {
2549+
"type": "object",
2550+
"description": "The structure of this object is service-specific"
2551+
},
25492552
"ODataCountResponse": {
25502553
"type": "integer",
25512554
"format": "int32"

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Basic.OpenApi.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,8 +1674,7 @@ components:
16741674
items:
16751675
$ref: '#/components/schemas/odata.error.detail'
16761676
innererror:
1677-
type: object
1678-
description: The structure of this object is service-specific
1677+
$ref: '#/components/schemas/odata.error.innererror'
16791678
odata.error.detail:
16801679
required:
16811680
- code
@@ -1689,6 +1688,9 @@ components:
16891688
target:
16901689
type: string
16911690
nullable: true
1691+
odata.error.innererror:
1692+
type: object
1693+
description: The structure of this object is service-specific
16921694
ODataCountResponse:
16931695
type: integer
16941696
format: int32

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@
4545
}
4646
},
4747
"innererror": {
48-
"description": "The structure of this object is service-specific",
49-
"type": "object"
48+
"$ref": "#/definitions/odata.error.innererror"
5049
}
5150
}
5251
},
@@ -68,6 +67,10 @@
6867
}
6968
}
7069
},
70+
"odata.error.innererror": {
71+
"description": "The structure of this object is service-specific",
72+
"type": "object"
73+
},
7174
"ODataCountResponse": {
7275
"format": "int32",
7376
"type": "integer"

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.V2.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ definitions:
3232
items:
3333
$ref: '#/definitions/odata.error.detail'
3434
innererror:
35-
description: The structure of this object is service-specific
36-
type: object
35+
$ref: '#/definitions/odata.error.innererror'
3736
odata.error.detail:
3837
required:
3938
- code
@@ -46,6 +45,9 @@ definitions:
4645
type: string
4746
target:
4847
type: string
48+
odata.error.innererror:
49+
description: The structure of this object is service-specific
50+
type: object
4951
ODataCountResponse:
5052
format: int32
5153
type: integer

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@
4848
}
4949
},
5050
"innererror": {
51-
"type": "object",
52-
"description": "The structure of this object is service-specific"
51+
"$ref": "#/components/schemas/odata.error.innererror"
5352
}
5453
}
5554
},
@@ -72,6 +71,10 @@
7271
}
7372
}
7473
},
74+
"odata.error.innererror": {
75+
"type": "object",
76+
"description": "The structure of this object is service-specific"
77+
},
7578
"ODataCountResponse": {
7679
"type": "integer",
7780
"format": "int32"

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Empty.OpenApi.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ components:
3333
items:
3434
$ref: '#/components/schemas/odata.error.detail'
3535
innererror:
36-
type: object
37-
description: The structure of this object is service-specific
36+
$ref: '#/components/schemas/odata.error.innererror'
3837
odata.error.detail:
3938
required:
4039
- code
@@ -48,6 +47,9 @@ components:
4847
target:
4948
type: string
5049
nullable: true
50+
odata.error.innererror:
51+
type: object
52+
description: The structure of this object is service-specific
5153
ODataCountResponse:
5254
type: integer
5355
format: int32

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5849,8 +5849,7 @@
58495849
}
58505850
},
58515851
"innererror": {
5852-
"description": "The structure of this object is service-specific",
5853-
"type": "object"
5852+
"$ref": "#/definitions/odata.error.innererror"
58545853
}
58555854
}
58565855
},
@@ -5872,6 +5871,10 @@
58725871
}
58735872
}
58745873
},
5874+
"odata.error.innererror": {
5875+
"description": "The structure of this object is service-specific",
5876+
"type": "object"
5877+
},
58755878
"ODataCountResponse": {
58765879
"format": "int32",
58775880
"type": "integer"

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.V2.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4291,8 +4291,7 @@ definitions:
42914291
items:
42924292
$ref: '#/definitions/odata.error.detail'
42934293
innererror:
4294-
description: The structure of this object is service-specific
4295-
type: object
4294+
$ref: '#/definitions/odata.error.innererror'
42964295
odata.error.detail:
42974296
required:
42984297
- code
@@ -4305,6 +4304,9 @@ definitions:
43054304
type: string
43064305
target:
43074306
type: string
4307+
odata.error.innererror:
4308+
description: The structure of this object is service-specific
4309+
type: object
43084310
ODataCountResponse:
43094311
format: int32
43104312
type: integer

test/Microsoft.OpenAPI.OData.Reader.Tests/Resources/Multiple.Schema.OpenApi.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6579,8 +6579,7 @@
65796579
}
65806580
},
65816581
"innererror": {
6582-
"type": "object",
6583-
"description": "The structure of this object is service-specific"
6582+
"$ref": "#/components/schemas/odata.error.innererror"
65846583
}
65856584
}
65866585
},
@@ -6603,6 +6602,10 @@
66036602
}
66046603
}
66056604
},
6605+
"odata.error.innererror": {
6606+
"type": "object",
6607+
"description": "The structure of this object is service-specific"
6608+
},
66066609
"ODataCountResponse": {
66076610
"type": "integer",
66086611
"format": "int32"

0 commit comments

Comments
 (0)