Skip to content

Commit a095dbf

Browse files
committed
Handle "Examples" in media type and parameter
add more tests
1 parent afd19c9 commit a095dbf

28 files changed

+1105
-241
lines changed

src/Microsoft.OpenApi.Readers/ParseNodes/AnyFieldMapParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal class AnyFieldMapParameter<T>
1414
/// </summary>
1515
public AnyFieldMapParameter(
1616
Func<T, IOpenApiAny> propertyGetter,
17-
Func<T, IOpenApiAny, IOpenApiAny> propertySetter,
17+
Action<T, IOpenApiAny> propertySetter,
1818
Func<T, OpenApiSchema> schemaGetter)
1919
{
2020
this.PropertyGetter = propertyGetter;
@@ -30,7 +30,7 @@ public AnyFieldMapParameter(
3030
/// <summary>
3131
/// Function to set the value of the property.
3232
/// </summary>
33-
public Func<T, IOpenApiAny, IOpenApiAny> PropertySetter { get; }
33+
public Action<T, IOpenApiAny> PropertySetter { get; }
3434

3535
/// <summary>
3636
/// Function to get the schema to apply to the property.

src/Microsoft.OpenApi.Readers/ParseNodes/AnyListFieldMapParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal class AnyListFieldMapParameter<T>
1515
/// </summary>
1616
public AnyListFieldMapParameter(
1717
Func<T, IList<IOpenApiAny>> propertyGetter,
18-
Func<T, IList<IOpenApiAny>, IList<IOpenApiAny>> propertySetter,
18+
Action<T, IList<IOpenApiAny>> propertySetter,
1919
Func<T, OpenApiSchema> schemaGetter)
2020
{
2121
this.PropertyGetter = propertyGetter;
@@ -31,7 +31,7 @@ public AnyListFieldMapParameter(
3131
/// <summary>
3232
/// Function to set the value of the property.
3333
/// </summary>
34-
public Func<T, IList<IOpenApiAny>, IList<IOpenApiAny>> PropertySetter { get; }
34+
public Action<T, IList<IOpenApiAny>> PropertySetter { get; }
3535

3636
/// <summary>
3737
/// Function to get the schema to apply to the property.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.OpenApi.Readers.ParseNodes
7+
{
8+
internal class AnyMapFieldMap<T, U> : Dictionary<string, AnyMapFieldMapParameter<T, U>>
9+
{
10+
11+
}
12+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using Microsoft.OpenApi.Any;
7+
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models;
9+
10+
namespace Microsoft.OpenApi.Readers.ParseNodes
11+
{
12+
internal class AnyMapFieldMapParameter<T, U>
13+
{
14+
/// <summary>
15+
/// Constructor
16+
/// </summary>
17+
public AnyMapFieldMapParameter(
18+
Func<T, IDictionary<string, U>> propertyMapGetter,
19+
Func<U, IOpenApiAny> propertyGetter,
20+
Action<U, IOpenApiAny> propertySetter,
21+
Func<T, OpenApiSchema> schemaGetter)
22+
{
23+
this.PropertyMapGetter = propertyMapGetter;
24+
this.PropertyGetter = propertyGetter;
25+
this.PropertySetter = propertySetter;
26+
this.SchemaGetter = schemaGetter;
27+
}
28+
29+
/// <summary>
30+
/// Function to retrieve the property that is a map from string to an inner element containing IOpenApiAny.
31+
/// </summary>
32+
public Func<T, IDictionary<string, U>> PropertyMapGetter { get; }
33+
34+
/// <summary>
35+
/// Function to retrieve the value of the property from an inner element.
36+
/// </summary>
37+
public Func<U, IOpenApiAny> PropertyGetter { get; }
38+
39+
/// <summary>
40+
/// Function to set the value of the property.
41+
/// </summary>
42+
public Action<U, IOpenApiAny> PropertySetter { get; }
43+
44+
/// <summary>
45+
/// Function to get the schema to apply to the property.
46+
/// </summary>
47+
public Func<T, OpenApiSchema> SchemaGetter { get; }
48+
}
49+
}

src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ internal static partial class OpenApiV2Deserializer
4848
{s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
4949
};
5050

51+
private static readonly AnyMapFieldMap<OpenApiMediaType, OpenApiExample> _mediaTypeAnyMapOpenApiExampleFields =
52+
new AnyMapFieldMap<OpenApiMediaType, OpenApiExample>
53+
{
54+
{
55+
OpenApiConstants.Examples,
56+
new AnyMapFieldMapParameter<OpenApiMediaType, OpenApiExample>(
57+
m => m.Examples,
58+
e => e.Value,
59+
(e, v) => e.Value = v,
60+
m => m.Schema)
61+
}
62+
};
63+
5164
private static void ProcessProduces(OpenApiResponse response, ParsingContext context)
5265
{
5366
var produces = context.GetFromTempStorage<List<string>>(TempStorageKeys.OperationProduces) ??
@@ -122,6 +135,11 @@ public static OpenApiResponse LoadResponse(ParseNode node)
122135

123136
ProcessProduces(response, node.Context);
124137

138+
foreach( var mediaType in response.Content.Values)
139+
{
140+
ProcessAnyMapFields(mapNode, mediaType, _mediaTypeAnyMapOpenApiExampleFields);
141+
}
142+
125143
return response;
126144
}
127145
}

src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,47 @@ private static void ProcessAnyListFields<T>(
100100
}
101101
}
102102

103+
private static void ProcessAnyMapFields<T, U>(
104+
MapNode mapNode,
105+
T domainObject,
106+
AnyMapFieldMap<T, U> anyMapFieldMap)
107+
{
108+
foreach (var anyMapFieldName in anyMapFieldMap.Keys.ToList())
109+
{
110+
try
111+
{
112+
var newProperty = new List<IOpenApiAny>();
113+
114+
mapNode.Context.StartObject(anyMapFieldName);
115+
116+
foreach (var propertyMapElement in anyMapFieldMap[anyMapFieldName].PropertyMapGetter(domainObject))
117+
{
118+
if (propertyMapElement.Value != null)
119+
{
120+
mapNode.Context.StartObject(propertyMapElement.Key);
121+
122+
var any = anyMapFieldMap[anyMapFieldName].PropertyGetter(propertyMapElement.Value);
123+
124+
var newAny = OpenApiAnyConverter.GetSpecificOpenApiAny(
125+
any,
126+
anyMapFieldMap[anyMapFieldName].SchemaGetter(domainObject));
127+
128+
anyMapFieldMap[anyMapFieldName].PropertySetter(propertyMapElement.Value, newAny);
129+
}
130+
}
131+
}
132+
catch (OpenApiException exception)
133+
{
134+
exception.Pointer = mapNode.Context.GetLocation();
135+
mapNode.Diagnostic.Errors.Add(new OpenApiError(exception));
136+
}
137+
finally
138+
{
139+
mapNode.Context.EndObject();
140+
}
141+
}
142+
}
143+
103144
public static IOpenApiAny LoadAny(ParseNode node)
104145
{
105146
return OpenApiAnyConverter.GetSpecificOpenApiAny(node.CreateAny());

src/Microsoft.OpenApi.Readers/V3/OpenApiMediaTypeDeserializer.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
using System;
5+
using System.Collections.Generic;
46
using System.Linq;
7+
using Microsoft.OpenApi.Any;
58
using Microsoft.OpenApi.Extensions;
69
using Microsoft.OpenApi.Models;
710
using Microsoft.OpenApi.Readers.ParseNodes;
@@ -60,6 +63,20 @@ internal static partial class OpenApiV3Deserializer
6063
}
6164
};
6265

66+
67+
private static readonly AnyMapFieldMap<OpenApiMediaType, OpenApiExample> _mediaTypeAnyMapOpenApiExampleFields =
68+
new AnyMapFieldMap<OpenApiMediaType, OpenApiExample>
69+
{
70+
{
71+
OpenApiConstants.Examples,
72+
new AnyMapFieldMapParameter<OpenApiMediaType, OpenApiExample>(
73+
m => m.Examples,
74+
e => e.Value,
75+
(e, v) => e.Value = v,
76+
m => m.Schema)
77+
}
78+
};
79+
6380
public static OpenApiMediaType LoadMediaType(ParseNode node)
6481
{
6582
var mapNode = node.CheckMapNode(OpenApiConstants.Content);
@@ -74,6 +91,7 @@ public static OpenApiMediaType LoadMediaType(ParseNode node)
7491
ParseMap(mapNode, mediaType, _mediaTypeFixedFields, _mediaTypePatternFields);
7592

7693
ProcessAnyFields(mapNode, mediaType, _mediaTypeAnyFields);
94+
ProcessAnyMapFields(mapNode, mediaType, _mediaTypeAnyMapOpenApiExampleFields);
7795

7896
return mediaType;
7997
}

src/Microsoft.OpenApi.Readers/V3/OpenApiParameterDeserializer.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ internal static partial class OpenApiV3Deserializer
126126
}
127127
};
128128

129+
private static readonly AnyMapFieldMap<OpenApiParameter, OpenApiExample> _parameterAnyMapOpenApiExampleFields =
130+
new AnyMapFieldMap<OpenApiParameter, OpenApiExample>
131+
{
132+
{
133+
OpenApiConstants.Examples,
134+
new AnyMapFieldMapParameter<OpenApiParameter, OpenApiExample>(
135+
m => m.Examples,
136+
e => e.Value,
137+
(e, v) => e.Value = v,
138+
m => m.Schema)
139+
}
140+
};
141+
129142
public static OpenApiParameter LoadParameter(ParseNode node)
130143
{
131144
var mapNode = node.CheckMapNode("parameter");
@@ -140,6 +153,7 @@ public static OpenApiParameter LoadParameter(ParseNode node)
140153

141154
ParseMap(mapNode, parameter, _parameterFixedFields, _parameterPatternFields);
142155
ProcessAnyFields(mapNode, parameter, _parameterAnyFields);
156+
ProcessAnyMapFields(mapNode, parameter, _parameterAnyMapOpenApiExampleFields);
143157

144158
return parameter;
145159
}

src/Microsoft.OpenApi.Readers/V3/OpenApiV3Deserializer.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,47 @@ private static void ProcessAnyListFields<T>(
100100
}
101101
}
102102

103+
private static void ProcessAnyMapFields<T, U>(
104+
MapNode mapNode,
105+
T domainObject,
106+
AnyMapFieldMap<T, U> anyMapFieldMap)
107+
{
108+
foreach (var anyMapFieldName in anyMapFieldMap.Keys.ToList())
109+
{
110+
try
111+
{
112+
var newProperty = new List<IOpenApiAny>();
113+
114+
mapNode.Context.StartObject(anyMapFieldName);
115+
116+
foreach (var propertyMapElement in anyMapFieldMap[anyMapFieldName].PropertyMapGetter(domainObject))
117+
{
118+
mapNode.Context.StartObject(propertyMapElement.Key);
119+
120+
if (propertyMapElement.Value != null)
121+
{
122+
var any = anyMapFieldMap[anyMapFieldName].PropertyGetter(propertyMapElement.Value);
123+
124+
var newAny = OpenApiAnyConverter.GetSpecificOpenApiAny(
125+
any,
126+
anyMapFieldMap[anyMapFieldName].SchemaGetter(domainObject));
127+
128+
anyMapFieldMap[anyMapFieldName].PropertySetter(propertyMapElement.Value, newAny);
129+
}
130+
}
131+
}
132+
catch (OpenApiException exception)
133+
{
134+
exception.Pointer = mapNode.Context.GetLocation();
135+
mapNode.Diagnostic.Errors.Add(new OpenApiError(exception));
136+
}
137+
finally
138+
{
139+
mapNode.Context.EndObject();
140+
}
141+
}
142+
}
143+
103144
private static RuntimeExpression LoadRuntimeExpression(ParseNode node)
104145
{
105146
var value = node.GetScalarValue();

src/Microsoft.OpenApi/Validations/OpenApiValidator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ public void AddError(OpenApiValidatorError error)
8383
/// <param name="item">The object to be validated</param>
8484
public override void Visit(OpenApiResponse item) => Validate(item);
8585

86+
/// <summary>
87+
/// Execute validation rules against an <see cref="OpenApiMediaType"/>
88+
/// </summary>
89+
/// <param name="item">The object to be validated</param>
90+
public override void Visit(OpenApiMediaType item) => Validate(item);
91+
8692
/// <summary>
8793
/// Execute validation rules against an <see cref="OpenApiResponses"/>
8894
/// </summary>

0 commit comments

Comments
 (0)