Skip to content

Commit 9fc3ed3

Browse files
committed
Changed reader to create unresolved references
1 parent 636cf08 commit 9fc3ed3

17 files changed

+221
-37
lines changed

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
6060
// Parse the OpenAPI Document
6161
var document = context.Parse(yamlDocument, diagnostic);
6262

63+
// Resolve References
64+
var resolver = new Resolver(document);
65+
var walker = new OpenApiWalker(resolver);
66+
walker.Walk(document);
67+
6368
// Validate the document
6469
var errors = document.Validate(_settings.RuleSet);
6570
foreach (var item in errors)

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

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,26 @@ public override Dictionary<string, T> CreateMap<T>(Func<MapNode, T> map)
7878
return nodes.ToDictionary(k => k.key, v => v.value);
7979
}
8080

81-
public override Dictionary<string, T> CreateMapWithReference<T>(
82-
ReferenceType referenceType,
83-
string refpointerbase,
84-
Func<MapNode, T> map)
85-
{
86-
var yamlMap = _node;
87-
if (yamlMap == null)
88-
{
89-
throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
90-
}
91-
92-
var nodes = yamlMap.Select(
93-
n => new
94-
{
95-
key = n.Key.GetScalarValue(),
96-
value = GetReferencedObject<T>(referenceType, refpointerbase + n.Key.GetScalarValue()) ??
97-
map(new MapNode(Context, Diagnostic, (YamlMappingNode)n.Value))
98-
});
99-
return nodes.ToDictionary(k => k.key, v => v.value);
100-
}
81+
// public override Dictionary<string, T> CreateMapWithReference<T>(
82+
// ReferenceType referenceType,
83+
// string refpointerbase,
84+
// Func<MapNode, T> map)
85+
// {
86+
// var yamlMap = _node;
87+
// if (yamlMap == null)
88+
// {
89+
// throw new OpenApiException($"Expected map at line {yamlMap.Start.Line} while parsing {typeof(T).Name}");
90+
// }
91+
92+
// var nodes = yamlMap.Select(
93+
// n => new
94+
// {
95+
// key = n.Key.GetScalarValue(),
96+
// value = GetReferencedObject<T>(referenceType, refpointerbase + n.Key.GetScalarValue()) ??
97+
// map(new MapNode(Context, Diagnostic, (YamlMappingNode)n.Value))
98+
// });
99+
// return nodes.ToDictionary(k => k.key, v => v.value);
100+
// }
101101

102102
public override Dictionary<string, T> CreateSimpleMap<T>(Func<ValueNode, T> map)
103103
{
@@ -133,12 +133,13 @@ public override string GetRaw()
133133
}
134134

135135
public T GetReferencedObject<T>(ReferenceType referenceType, string referenceId)
136-
where T : IOpenApiReferenceable
136+
where T : IOpenApiReferenceable, new()
137137
{
138-
return (T)Context.GetReferencedObject(
139-
Diagnostic,
140-
referenceType,
141-
referenceId);
138+
return new T()
139+
{
140+
UnresolvedReference = true,
141+
Reference = Context.VersionService.ConvertToOpenApiReference(referenceId,referenceType)
142+
};
142143
}
143144

144145
public string GetReferencePointer()

src/Microsoft.OpenApi.Readers/ParsingContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ public class ParsingContext
2525
private readonly Dictionary<string, IOpenApiReferenceable> _referenceStore = new Dictionary<string, IOpenApiReferenceable>();
2626
private readonly Dictionary<string, object> _tempStorage = new Dictionary<string, object>();
2727
private IOpenApiVersionService _versionService;
28-
private readonly Dictionary<string, Stack<string>> _loopStacks = new Dictionary<string, Stack<string>>(); internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
28+
private readonly Dictionary<string, Stack<string>> _loopStacks = new Dictionary<string, Stack<string>>();
29+
internal Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, IOpenApiExtension>>();
2930
internal RootNode RootNode { get; set; }
3031
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();
3132

32-
3333
/// <summary>
3434
/// Initiates the parsing process. Not thread safe and should only be called once on a parsing context
3535
/// </summary>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Microsoft.OpenApi.Interfaces;
10+
using Microsoft.OpenApi.Models;
11+
using Microsoft.OpenApi.Services;
12+
13+
namespace Microsoft.OpenApi.Readers
14+
{
15+
16+
internal class Resolver : OpenApiVisitorBase
17+
{
18+
private OpenApiDocument _currentDocument;
19+
20+
public Resolver(OpenApiDocument currentDocument) // In future pass in Workbench for remote references
21+
{
22+
_currentDocument = currentDocument;
23+
}
24+
25+
public override void Visit(OpenApiMediaType mediaType)
26+
{
27+
if (IsReference(mediaType.Schema))
28+
{
29+
mediaType.Schema = ResolveReference<OpenApiSchema>(mediaType.Schema.Reference);
30+
}
31+
}
32+
33+
private T ResolveReference<T>(OpenApiReference reference) where T : class
34+
{
35+
if (string.IsNullOrEmpty(reference.ExternalResource))
36+
{
37+
return _currentDocument.ResolveReference(reference) as T;
38+
}
39+
else
40+
{
41+
// TODO
42+
return default(T);
43+
}
44+
}
45+
46+
private bool IsReference(IOpenApiReferenceable possibleReference)
47+
{
48+
return (possibleReference != null && possibleReference.Reference != null);
49+
}
50+
}
51+
}

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,20 +244,11 @@ public static OpenApiSchema LoadSchema(ParseNode node)
244244

245245
if (pointer != null)
246246
{
247-
//if (node.Context.PushLoop(schemaLoopId, pointer))
248-
//{
249-
// var schema = mapNode.GetReferencedObject<OpenApiSchema>(ReferenceType.Schema, pointer);
250-
// node.Context.PopLoop(schemaLoopId);
251-
// return schema;
252-
//} else
253-
//{
254-
// node.Context.ClearLoop(schemaLoopId);
255-
//TODO. How do we make the object graph have a cycle. Or should we break the cycle in the graph?
256247
return new OpenApiSchema()
257248
{
249+
UnresolvedReference = true,
258250
Reference = node.Context.VersionService.ConvertToOpenApiReference(pointer,ReferenceType.Schema)
259251
};
260-
//}
261252
}
262253

263254
var domainObject = new OpenApiSchema();

src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ namespace Microsoft.OpenApi.Interfaces
1111
/// </summary>
1212
public interface IOpenApiReferenceable : IOpenApiSerializable
1313
{
14+
15+
/// <summary>
16+
/// Indicates if object is populated with data or is just a reference to the data
17+
/// </summary>
18+
bool UnresolvedReference { get; set;}
19+
1420
/// <summary>
1521
/// Reference object.
1622
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiCallback.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public class OpenApiCallback : IOpenApiSerializable, IOpenApiReferenceable, IOpe
2020
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; set; }
2121
= new Dictionary<RuntimeExpression, OpenApiPathItem>();
2222

23+
/// <summary>
24+
/// Indicates if object is populated with data or is just a reference to the data
25+
/// </summary>
26+
public bool UnresolvedReference { get; set;}
27+
2328
/// <summary>
2429
/// Reference pointer.
2530
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using Microsoft.OpenApi.Any;
8+
using Microsoft.OpenApi.Exceptions;
89
using Microsoft.OpenApi.Interfaces;
910
using Microsoft.OpenApi.Writers;
1011

@@ -264,5 +265,84 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>
264265
// schemes
265266
writer.WriteOptionalCollection(OpenApiConstants.Schemes, schemes, (w, s) => w.WriteValue(s));
266267
}
268+
269+
/// <summary>
270+
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
271+
/// </summary>
272+
public IOpenApiReferenceable ResolveReference(OpenApiReference reference)
273+
{
274+
275+
if (reference == null)
276+
{
277+
return null;
278+
}
279+
280+
if (reference.IsExternal)
281+
{
282+
// Should not attempt to resolve external references against a single document.
283+
throw new ArgumentException(); //TODO Add error message
284+
}
285+
286+
if (!reference.Type.HasValue)
287+
{
288+
throw new ArgumentException("Local reference must have type specified.");
289+
}
290+
291+
// Special case for Tag
292+
if (reference.Type == ReferenceType.Tag)
293+
{
294+
foreach (var tag in this.Tags)
295+
{
296+
if (tag.Name == reference.Id)
297+
{
298+
return tag;
299+
}
300+
}
301+
302+
return null;
303+
}
304+
305+
try
306+
{
307+
switch (reference.Type)
308+
{
309+
case ReferenceType.Schema:
310+
return this.Components.Schemas[reference.Id];
311+
312+
case ReferenceType.Response:
313+
return this.Components.Responses[reference.Id];
314+
315+
case ReferenceType.Parameter:
316+
return this.Components.Parameters[reference.Id];
317+
318+
case ReferenceType.Example:
319+
return this.Components.Examples[reference.Id];
320+
321+
case ReferenceType.RequestBody:
322+
return this.Components.RequestBodies[reference.Id];
323+
324+
case ReferenceType.Header:
325+
return this.Components.Headers[reference.Id];
326+
327+
case ReferenceType.SecurityScheme:
328+
return this.Components.SecuritySchemes[reference.Id];
329+
330+
case ReferenceType.Link:
331+
return this.Components.Links[reference.Id];
332+
333+
case ReferenceType.Callback:
334+
return this.Components.Callbacks[reference.Id];
335+
336+
default:
337+
// TODO: Create resource
338+
throw new OpenApiException("Invalid Reference type");
339+
}
340+
} catch(KeyNotFoundException)
341+
{
342+
throw new OpenApiException("Invalid Reference id");
343+
}
344+
345+
}
346+
267347
}
268348
}

src/Microsoft.OpenApi/Models/OpenApiExample.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public class OpenApiExample : IOpenApiSerializable, IOpenApiReferenceable, IOpen
4949
/// </summary>
5050
public OpenApiReference Reference { get; set; }
5151

52+
/// <summary>
53+
/// Indicates object is a placeholder reference to an actual object and does not contain valid data.
54+
/// </summary>
55+
public bool UnresolvedReference { get; set; } = false;
56+
5257
/// <summary>
5358
/// Serialize <see cref="OpenApiExample"/> to Open Api v3.0
5459
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiHeader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ namespace Microsoft.OpenApi.Models
1515
/// </summary>
1616
public class OpenApiHeader : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible
1717
{
18+
/// <summary>
19+
/// Indicates if object is populated with data or is just a reference to the data
20+
/// </summary>
21+
public bool UnresolvedReference { get; set;}
22+
1823
/// <summary>
1924
/// Reference pointer.
2025
/// </summary>

0 commit comments

Comments
 (0)