Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ce5c724
Add methods to set and retrieve components registries
irvinesunday Mar 5, 2024
94028ae
Create new property that will capture unique document ID or base Uri
irvinesunday Mar 5, 2024
e27a4e8
Update workspace
irvinesunday Mar 18, 2024
95710e5
Update reference resolver tests
irvinesunday Mar 25, 2024
76512a2
Register components in document deserializers
irvinesunday Mar 25, 2024
4bd1685
Add document to its workspace when created via parameterless constructor
irvinesunday Mar 25, 2024
5cea77e
Use document Workspace instance
irvinesunday Mar 27, 2024
351baec
Use the document Workspace instance already created in the ctor
irvinesunday Mar 27, 2024
a5af833
Don't register components directly
irvinesunday Mar 27, 2024
8acb0d2
Remove ref resolution from doc.; update ctor
irvinesunday Mar 27, 2024
2cfaf7a
Update workspace; add new methods
irvinesunday Mar 27, 2024
9bdbac6
Update tests
irvinesunday Mar 27, 2024
74e4b4d
Merge remote-tracking branch 'origin/release/2.0.0' into is/component…
irvinesunday Mar 27, 2024
dd3f13c
Add JsonSchema reference resolution to OpenApiDocument class
irvinesunday Mar 28, 2024
0e3bf94
Remove unnecessary code; revert BaseUrl value
irvinesunday Mar 28, 2024
276e5ee
Update tests
irvinesunday Mar 28, 2024
14597de
Remove unused code
irvinesunday Mar 28, 2024
ea186d8
Revert deleted code
irvinesunday Mar 28, 2024
6ebc492
Update reference test
irvinesunday Mar 29, 2024
4f09473
Update test
irvinesunday Mar 29, 2024
4e480ed
Update test file and test
irvinesunday Mar 29, 2024
1b286e5
Update models
irvinesunday Apr 2, 2024
e7ff360
Update tests
irvinesunday Apr 2, 2024
ef633cf
Update Public Api
irvinesunday Apr 2, 2024
dbfcf0a
Resolve public Api
irvinesunday Apr 2, 2024
464bd21
Register local refs within external documents with unique GUID
irvinesunday Apr 2, 2024
77d1e49
Refactor functions
irvinesunday Apr 2, 2024
9a9827a
Use unique document ids in URIs in components registry and ref resolu…
irvinesunday Apr 3, 2024
feb674f
Merge branch release/2.0.0
irvinesunday Apr 3, 2024
6954790
Remove unnecessary code
irvinesunday Apr 3, 2024
f6912ab
Resolve merge conflicts with release/2.0.0 branch
irvinesunday Apr 4, 2024
33236ad
Merge method resolving JsonSchemas with SetHostDocument
irvinesunday Apr 4, 2024
977e8c6
Update XML summary
irvinesunday Apr 4, 2024
21bed6b
Update PublicAPI
irvinesunday Apr 4, 2024
d744e64
Merge remote-tracking branch 'origin/release/2.0.0' into is/component…
irvinesunday Apr 4, 2024
54712c1
Update Public API
irvinesunday Apr 4, 2024
11b3399
Update comment
irvinesunday Apr 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,11 @@ public static class OpenApiConstants
/// </summary>
public const string V2ReferenceUri = "https://registry/definitions/";

/// <summary>
/// The default registry uri for OpenApi documents and workspaces
/// </summary>
public const string BaseRegistryUri = "http://openapi.net/";

#region V2.0

/// <summary>
Expand Down
116 changes: 41 additions & 75 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Json.Schema;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Reader;
using Microsoft.OpenApi.Services;
Expand Down Expand Up @@ -94,8 +94,12 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IBaseDo
/// <summary>
/// Parameter-less constructor
/// </summary>
public OpenApiDocument() { }

public OpenApiDocument()
{
Workspace = new OpenApiWorkspace();
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid().ToString());

Check notice

Code scanning / CodeQL

Redundant ToString() call

Redundant call to 'ToString' on a String object.
}

/// <summary>
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
/// </summary>
Expand Down Expand Up @@ -478,6 +482,32 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference)
return ResolveReference(reference, false);
}

/// <summary>
/// Resolves JsonSchema refs
/// </summary>
/// <param name="referenceUri"></param>
/// <returns>A JsonSchema ref.</returns>
public JsonSchema ResolveJsonSchemaReference(Uri referenceUri)
{
string uriLocation;
string id = referenceUri.OriginalString.Split('/')?.Last();
string relativePath = "/components/" + ReferenceType.Schema.GetDisplayName() + "/" + id;
if (referenceUri.OriginalString.StartsWith("#"))
{
// Local reference
uriLocation = BaseUri + relativePath;
}
else
{
// External reference
var externalUri = referenceUri.OriginalString.Split('#').First();
var externalDocId = Workspace.GetDocumentId(externalUri);
uriLocation = externalDocId + relativePath;
}

return (JsonSchema)Workspace.ResolveReference<IBaseDocument>(uriLocation);
}

/// <summary>
/// Takes in an OpenApi document instance and generates its hash value
/// </summary>
Expand Down Expand Up @@ -522,16 +552,6 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool
return null;
}

// Todo: Verify if we need to check to see if this external reference is actually targeted at this document.
if (useExternal)
{
if (this.Workspace == null)
{
throw new ArgumentException(Properties.SRResource.WorkspaceRequredForExternalReferenceResolution);
}
return this.Workspace.ResolveReference(reference);
}

if (!reference.Type.HasValue)
{
throw new ArgumentException(Properties.SRResource.LocalReferenceRequiresType);
Expand All @@ -552,70 +572,16 @@ internal IOpenApiReferenceable ResolveReference(OpenApiReference reference, bool
return null;
}

if (this.Components == null)
{
throw new OpenApiException(string.Format(Properties.SRResource.InvalidReferenceId, reference.Id));
}
string uriLocation;
string relativePath = "/components/" + reference.Type.GetDisplayName() + "/" + reference.Id;

try
{
switch (reference.Type)
{
case ReferenceType.PathItem:
var resolvedPathItem = this.Components.PathItems[reference.Id];
resolvedPathItem.Description = reference.Description ?? resolvedPathItem.Description;
resolvedPathItem.Summary = reference.Summary ?? resolvedPathItem.Summary;
return resolvedPathItem;

case ReferenceType.Response:
var resolvedResponse = this.Components.Responses[reference.Id];
resolvedResponse.Description = reference.Description ?? resolvedResponse.Description;
return resolvedResponse;

case ReferenceType.Parameter:
var resolvedParameter = this.Components.Parameters[reference.Id];
resolvedParameter.Description = reference.Description ?? resolvedParameter.Description;
return resolvedParameter;

case ReferenceType.Example:
var resolvedExample = this.Components.Examples[reference.Id];
resolvedExample.Summary = reference.Summary ?? resolvedExample.Summary;
resolvedExample.Description = reference.Description ?? resolvedExample.Description;
return resolvedExample;

case ReferenceType.RequestBody:
var resolvedRequestBody = this.Components.RequestBodies[reference.Id];
resolvedRequestBody.Description = reference.Description ?? resolvedRequestBody.Description;
return resolvedRequestBody;

case ReferenceType.Header:
var resolvedHeader = this.Components.Headers[reference.Id];
resolvedHeader.Description = reference.Description ?? resolvedHeader.Description;
return resolvedHeader;

case ReferenceType.SecurityScheme:
var resolvedSecurityScheme = this.Components.SecuritySchemes[reference.Id];
resolvedSecurityScheme.Description = reference.Description ?? resolvedSecurityScheme.Description;
return resolvedSecurityScheme;

case ReferenceType.Link:
var resolvedLink = this.Components.Links[reference.Id];
resolvedLink.Description = reference.Description ?? resolvedLink.Description;
return resolvedLink;

case ReferenceType.Callback:
return this.Components.Callbacks[reference.Id];

default:
throw new OpenApiException(Properties.SRResource.InvalidReferenceType);
}
}
catch (KeyNotFoundException)
{
throw new OpenApiException(string.Format(Properties.SRResource.InvalidReferenceId, reference.Id));
}
}
uriLocation = useExternal
? Workspace.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath
: BaseUri + relativePath;

return Workspace.ResolveReference<IOpenApiReferenceable>(uriLocation);
}

/// <summary>
/// Parses a local file path or Url into an Open API document.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private OpenApiCallback Target
/// <param name="hostDocument">The host OpenAPI document.</param>
/// <param name="externalResource">Optional: External resource in the reference.
/// It may be:
/// 1. a absolute/relative file path, for example: ../commons/pet.json
/// 1. an absolute/relative file path, for example: ../commons/pet.json
/// 2. a Url, for example: http://localhost/pet.json
/// </param>
public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
Expand All @@ -16,7 +17,7 @@ internal class OpenApiRemoteReferenceCollector : OpenApiVisitorBase
private readonly Dictionary<string, OpenApiReference> _references = new();

/// <summary>
/// List of external references collected from OpenApiDocument
/// List of all internal and external references collected from OpenApiDocument
/// </summary>
public IEnumerable<OpenApiReference> References
{
Expand All @@ -32,13 +33,13 @@ public IEnumerable<OpenApiReference> References
/// <param name="referenceable"></param>
public override void Visit(IOpenApiReferenceable referenceable)
{
AddReference(referenceable.Reference);
AddExternalReferences(referenceable.Reference);
}

/// <summary>
/// Collect external reference
/// Collect internal and external references
/// </summary>
private void AddReference(OpenApiReference reference)
private void AddExternalReferences(OpenApiReference reference)
{
if (reference is {IsExternal: true} &&
!_references.ContainsKey(reference.ExternalResource))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ internal async Task<OpenApiDiagnostic> LoadAsync(OpenApiReference reference,
OpenApiDiagnostic diagnostic = null,
CancellationToken cancellationToken = default)
{
_workspace.AddDocument(reference.ExternalResource, document);
_workspace.AddDocumentId(reference.ExternalResource, document.BaseUri);
_workspace.RegisterComponents(document);
document.Workspace = _workspace;

// Collect remote references by walking document
Expand All @@ -39,6 +40,7 @@ internal async Task<OpenApiDiagnostic> LoadAsync(OpenApiReference reference,
// Walk references
foreach (var item in referenceCollector.References)
{

// If not already in workspace, load it and process references
if (!_workspace.Contains(item.ExternalResource))
{
Expand All @@ -51,7 +53,7 @@ internal async Task<OpenApiDiagnostic> LoadAsync(OpenApiReference reference,
}
if (result.OpenApiDocument != null)
{
var loadDiagnostic = await LoadAsync(item, result.OpenApiDocument, format, diagnostic, cancellationToken);
var loadDiagnostic = await LoadAsync(item, result.OpenApiDocument, format, diagnostic, cancellationToken);
diagnostic = loadDiagnostic;
}
}
Expand Down
24 changes: 13 additions & 11 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,36 +236,38 @@ private static string BuildUrl(string scheme, string host, string basePath)

public static OpenApiDocument LoadOpenApi(RootNode rootNode)
{
var openApidoc = new OpenApiDocument();
var openApiDoc = new OpenApiDocument();

var openApiNode = rootNode.GetMap();

ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
ParseMap(openApiNode, openApiDoc, _openApiFixedFields, _openApiPatternFields);

if (openApidoc.Paths != null)
if (openApiDoc.Paths != null)
{
ProcessResponsesMediaTypes(
rootNode.GetMap(),
openApidoc.Paths.Values
openApiDoc.Paths.Values
.SelectMany(path => path.Operations?.Values ?? Enumerable.Empty<OpenApiOperation>())
.SelectMany(operation => operation.Responses?.Values ?? Enumerable.Empty<OpenApiResponse>()),
openApiNode.Context);
}

ProcessResponsesMediaTypes(rootNode.GetMap(), openApidoc.Components?.Responses?.Values, openApiNode.Context);
ProcessResponsesMediaTypes(rootNode.GetMap(), openApiDoc.Components?.Responses?.Values, openApiNode.Context);

// Post Process OpenApi Object
if (openApidoc.Servers == null)
if (openApiDoc.Servers == null)
{
openApidoc.Servers = new List<OpenApiServer>();
openApiDoc.Servers = new List<OpenApiServer>();
}

MakeServers(openApidoc.Servers, openApiNode.Context, rootNode);
MakeServers(openApiDoc.Servers, openApiNode.Context, rootNode);

FixRequestBodyReferences(openApidoc);
RegisterComponentsSchemasInGlobalRegistry(openApidoc.Components?.Schemas);
FixRequestBodyReferences(openApiDoc);

return openApidoc;
// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApiDoc;
}

private static void ProcessResponsesMediaTypes(MapNode mapNode, IEnumerable<OpenApiResponse> responses, ParsingContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ public static OpenApiComponents LoadComponents(ParseNode node)
var components = new OpenApiComponents();

ParseMap(mapNode, components, _componentsFixedFields, _componentsPatternFields);

foreach (var schema in components.Schemas)
{
var refUri = new Uri(OpenApiConstants.V3ReferenceUri + schema.Key);
SchemaRegistry.Global.Register(refUri, schema.Value);
}

return components;
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/Microsoft.OpenApi/Reader/V3/OpenApiDocumentDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Reader.ParseNodes;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Reader.V3
{
Expand Down Expand Up @@ -46,12 +47,15 @@ internal static partial class OpenApiV3Deserializer

public static OpenApiDocument LoadOpenApi(RootNode rootNode)
{
var openApidoc = new OpenApiDocument();
var openApiDoc = new OpenApiDocument();
var openApiNode = rootNode.GetMap();

ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
ParseMap(openApiNode, openApiDoc, _openApiFixedFields, _openApiPatternFields);

return openApidoc;
// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApiDoc;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ public static OpenApiComponents LoadComponents(ParseNode node)

ParseMap(mapNode, components, _componentsFixedFields, _componentsPatternFields);

foreach (var schema in components.Schemas)
{
var refUri = new Uri(OpenApiConstants.V3ReferenceUri + schema.Key);
SchemaRegistry.Global.Register(refUri, schema.Value);
}

return components;
}
}
Expand Down
15 changes: 10 additions & 5 deletions src/Microsoft.OpenApi/Reader/V31/OpenApiDocumentDeserializer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Microsoft.OpenApi.Extensions;
using System;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Reader.ParseNodes;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Reader.V31
{
Expand All @@ -9,7 +11,7 @@ namespace Microsoft.OpenApi.Reader.V31
/// runtime Open API object model.
/// </summary>
internal static partial class OpenApiV31Deserializer
{
{
private static readonly FixedFieldMap<OpenApiDocument> _openApiFixedFields = new()
{
{
Expand Down Expand Up @@ -45,12 +47,15 @@ internal static partial class OpenApiV31Deserializer

public static OpenApiDocument LoadOpenApi(RootNode rootNode)
{
var openApidoc = new OpenApiDocument();
var openApiDoc = new OpenApiDocument();
var openApiNode = rootNode.GetMap();

ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
ParseMap(openApiNode, openApiDoc, _openApiFixedFields, _openApiPatternFields);

// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApidoc;
return openApiDoc;
}
}
}
Loading