forked from microsoft/OpenAPI.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOpenApiV3VersionService.cs
More file actions
228 lines (207 loc) · 9.6 KB
/
OpenApiV3VersionService.cs
File metadata and controls
228 lines (207 loc) · 9.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
using Microsoft.OpenApi.Reader.ParseNodes;
namespace Microsoft.OpenApi.Reader.V3
{
/// <summary>
/// The version service for the Open API V3.0.
/// </summary>
internal class OpenApiV3VersionService : IOpenApiVersionService
{
public OpenApiDiagnostic Diagnostic { get; }
private static readonly char[] _pathSeparator = new char[] { '/' };
/// <summary>
/// Create Parsing Context
/// </summary>
/// <param name="diagnostic">Provide instance for diagnostic object for collecting and accessing information about the parsing.</param>
public OpenApiV3VersionService(OpenApiDiagnostic diagnostic)
{
Diagnostic = diagnostic;
}
private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object>> _loaders = new()
{
[typeof(OpenApiAny)] = OpenApiV3Deserializer.LoadAny,
[typeof(OpenApiCallback)] = OpenApiV3Deserializer.LoadCallback,
[typeof(OpenApiComponents)] = OpenApiV3Deserializer.LoadComponents,
[typeof(OpenApiContact)] = OpenApiV3Deserializer.LoadContact,
[typeof(OpenApiDiscriminator)] = OpenApiV3Deserializer.LoadDiscriminator,
[typeof(OpenApiEncoding)] = OpenApiV3Deserializer.LoadEncoding,
[typeof(OpenApiExample)] = OpenApiV3Deserializer.LoadExample,
[typeof(OpenApiExternalDocs)] = OpenApiV3Deserializer.LoadExternalDocs,
[typeof(OpenApiHeader)] = OpenApiV3Deserializer.LoadHeader,
[typeof(OpenApiInfo)] = OpenApiV3Deserializer.LoadInfo,
[typeof(OpenApiLicense)] = OpenApiV3Deserializer.LoadLicense,
[typeof(OpenApiLink)] = OpenApiV3Deserializer.LoadLink,
[typeof(OpenApiMediaType)] = OpenApiV3Deserializer.LoadMediaType,
[typeof(OpenApiOAuthFlow)] = OpenApiV3Deserializer.LoadOAuthFlow,
[typeof(OpenApiOAuthFlows)] = OpenApiV3Deserializer.LoadOAuthFlows,
[typeof(OpenApiOperation)] = OpenApiV3Deserializer.LoadOperation,
[typeof(OpenApiParameter)] = OpenApiV3Deserializer.LoadParameter,
[typeof(OpenApiPathItem)] = OpenApiV3Deserializer.LoadPathItem,
[typeof(OpenApiPaths)] = OpenApiV3Deserializer.LoadPaths,
[typeof(OpenApiRequestBody)] = OpenApiV3Deserializer.LoadRequestBody,
[typeof(OpenApiResponse)] = OpenApiV3Deserializer.LoadResponse,
[typeof(OpenApiResponses)] = OpenApiV3Deserializer.LoadResponses,
[typeof(OpenApiSchema)] = OpenApiV3Deserializer.LoadSchema,
[typeof(OpenApiSecurityRequirement)] = OpenApiV3Deserializer.LoadSecurityRequirement,
[typeof(OpenApiSecurityScheme)] = OpenApiV3Deserializer.LoadSecurityScheme,
[typeof(OpenApiServer)] = OpenApiV3Deserializer.LoadServer,
[typeof(OpenApiServerVariable)] = OpenApiV3Deserializer.LoadServerVariable,
[typeof(OpenApiTag)] = OpenApiV3Deserializer.LoadTag,
[typeof(OpenApiXml)] = OpenApiV3Deserializer.LoadXml
};
/// <summary>
/// Parse the string to a <see cref="OpenApiReference"/> object.
/// </summary>
/// <param name="reference">The URL of the reference</param>
/// <param name="type">The type of object referenced based on the context of the reference</param>
/// <param name="summary"></param>
/// <param name="description"></param>
public OpenApiReference ConvertToOpenApiReference(
string reference,
ReferenceType? type,
string summary = null,
string description = null)
{
if (!string.IsNullOrWhiteSpace(reference))
{
var segments = reference.Split('#');
if (segments.Length == 1)
{
if (type is ReferenceType.Tag or ReferenceType.SecurityScheme)
{
return new()
{
Type = type,
Id = reference
};
}
// Either this is an external reference as an entire file
// or a simple string-style reference for tag and security scheme.
return new()
{
Type = type,
ExternalResource = segments[0]
};
}
else if (segments.Length == 2)
{
if (reference.StartsWith("#", StringComparison.OrdinalIgnoreCase))
{
// "$ref": "#/components/schemas/Pet"
try
{
return ParseLocalReference(segments[1]);
}
catch (OpenApiException ex)
{
Diagnostic.Errors.Add(new(ex));
}
}
// Where fragments point into a non-OpenAPI document, the id will be the complete fragment identifier
var id = segments[1];
var isFragment = false;
// $ref: externalSource.yaml#/Pet
if (id.StartsWith("/components/", StringComparison.Ordinal))
{
var localSegments = segments[1].Split('/');
localSegments[2].TryGetEnumFromDisplayName<ReferenceType>(out var referencedType);
if (type == null)
{
type = referencedType;
}
else
{
if (type != referencedType)
{
throw new OpenApiException("Referenced type mismatch");
}
}
id = localSegments[3];
}
else if (id.StartsWith("/paths/", StringComparison.Ordinal))
{
var localSegments = segments[1].Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries);
if (localSegments.Length == 2)
{
// The reference of a path may contain JSON escape character ~1 for the forward-slash character, replace this otherwise
// the reference cannot be resolved.
id = localSegments[1].Replace("~1", "/");
}
else
{
throw new OpenApiException("Referenced Path mismatch");
}
}
else
{
isFragment = true;
}
var openApiReference = new OpenApiReference
{
ExternalResource = segments[0],
Type = type,
Id = id,
IsFragment = isFragment,
};
return openApiReference;
}
}
throw new OpenApiException(string.Format(SRResource.ReferenceHasInvalidFormat, reference));
}
public OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
{
return OpenApiV3Deserializer.LoadOpenApi(rootNode, location);
}
public T LoadElement<T>(ParseNode node, OpenApiDocument doc) where T : IOpenApiElement
{
return (T)_loaders[typeof(T)](node, doc);
}
/// <inheritdoc />
public string GetReferenceScalarValues(MapNode mapNode, string scalarValue)
{
if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase)) &&
mapNode
.Where(x => x.Name.Equals(scalarValue))
.Select(static x => x.Value)
.OfType<ValueNode>().FirstOrDefault() is {} valueNode)
{
return valueNode.GetScalarValue();
}
return null;
}
private OpenApiReference ParseLocalReference(string localReference)
{
if (string.IsNullOrWhiteSpace(localReference))
{
throw new ArgumentException(string.Format(SRResource.ArgumentNullOrWhiteSpace, nameof(localReference)));
}
var segments = localReference.Split('/');
if (segments.Length == 4 && segments[1] == "components") // /components/{type}/pet
{
segments[2].TryGetEnumFromDisplayName<ReferenceType>(out var referenceType);
var refId = segments[3];
if (segments[2] == "pathItems")
{
refId = "/" + segments[3];
}
var parsedReference = new OpenApiReference
{
Type = referenceType,
Id = refId
};
return parsedReference;
}
throw new OpenApiException(string.Format(SRResource.ReferenceHasInvalidFormat, localReference));
}
}
}