Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates component registries in the OpenApiWorkspace for $ref resolution #1609

Merged
merged 37 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 31 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 @@ -618,13 +618,18 @@
/// <summary>
/// Field: V3 JsonSchema Reference Uri
/// </summary>
public const string V3ReferenceUri = "https://registry/components/schemas/";

Check warning on line 621 in src/Microsoft.OpenApi/Models/OpenApiConstants.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor your code not to use hardcoded absolute paths or URIs. (https://rules.sonarsource.com/csharp/RSPEC-1075)

/// <summary>
/// Field: V2 JsonSchema Reference Uri
/// </summary>
public const string V2ReferenceUri = "https://registry/definitions/";

Check warning on line 626 in src/Microsoft.OpenApi/Models/OpenApiConstants.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor your code not to use hardcoded absolute paths or URIs. (https://rules.sonarsource.com/csharp/RSPEC-1075)

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

#region V2.0

/// <summary>
Expand Down
109 changes: 44 additions & 65 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 @@
/// <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 Note

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

/// <summary>
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
/// </summary>
Expand Down Expand Up @@ -124,7 +128,7 @@

writer.WriteStartObject();

// openApi;

Check warning on line 131 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)
writer.WriteProperty(OpenApiConstants.OpenApi, "3.1.0");

// jsonSchemaDialect
Expand Down Expand Up @@ -447,12 +451,12 @@
/// This method will be replaced by a LoadExternalReferences in the next major update to this library.
/// Resolving references at load time is going to go away.
/// </remarks>
public IEnumerable<OpenApiError> ResolveReferences()
public IEnumerable<OpenApiError> ResolveJsonSchemaReferences()
{
var resolver = new OpenApiReferenceResolver(this, false);
var walker = new OpenApiWalker(resolver);
var jsonSchemaResolver = new JsonSchemaReferenceResolver(this);
var walker = new OpenApiWalker(jsonSchemaResolver);
walker.Walk(this);
return resolver.Errors;
return jsonSchemaResolver.Errors;
}

/// <summary>
Expand Down Expand Up @@ -488,6 +492,32 @@
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 @@ -532,16 +562,6 @@
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 @@ -562,51 +582,16 @@
return null;
}

if (this.Components == null)
{
throw new OpenApiException(string.Format(Properties.SRResource.InvalidReferenceId, reference.Id));
}

try
{
switch (reference.Type)
{
case ReferenceType.PathItem:
return Components.PathItems[reference.Id];
case ReferenceType.Response:
return Components.Responses[reference.Id];

case ReferenceType.Parameter:
return Components.Parameters[reference.Id];

case ReferenceType.Example:
return Components.Examples[reference.Id];

case ReferenceType.RequestBody:
return Components.RequestBodies[reference.Id];

case ReferenceType.Header:
return Components.Headers[reference.Id];

case ReferenceType.SecurityScheme:
return Components.SecuritySchemes[reference.Id];
string uriLocation;
string relativePath = "/components/" + reference.Type.GetDisplayName() + "/" + reference.Id;

case ReferenceType.Link:
return Components.Links[reference.Id];
uriLocation = useExternal
? Workspace.GetDocumentId(reference.ExternalResource)?.OriginalString + relativePath
: BaseUri + relativePath;

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

default:
throw new OpenApiException(Properties.SRResource.InvalidReferenceType);
}
}
catch (KeyNotFoundException)
{
throw new OpenApiException(string.Format(Properties.SRResource.InvalidReferenceId, reference.Id));
}
return Workspace.ResolveReference<IOpenApiReferenceable>(uriLocation);
}

/// <summary>
/// Parses a local file path or Url into an Open API document.
/// </summary>
Expand Down Expand Up @@ -707,12 +692,6 @@
{
throw new NotImplementedException();
}

internal JsonSchema ResolveJsonSchemaReference(Uri reference)
{
var referencePath = string.Concat("https://registry", reference.OriginalString.Split('#').Last());
return (JsonSchema)SchemaRegistry.Global.Get(new Uri(referencePath));
}
}

internal class FindSchemaReferences : OpenApiVisitorBase
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/OpenApiExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
{
Summary = example?.Summary ?? Summary;
Description = example?.Description ?? Description;
Value = JsonNodeCloneHelper.Clone(example?.Value);
Value = example?.Value ?? JsonNodeCloneHelper.Clone(example?.Value);

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.

Check failure

Code scanning / CodeQL

Useless ?? expression Error

Both operands of this null-coalescing expression access the same variable or property.
ExternalValue = example?.ExternalValue ?? ExternalValue;
Extensions = example?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(example.Extensions) : null;
Reference = example?.Reference != null ? new(example?.Reference) : null;
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@
Style = header?.Style ?? Style;
Explode = header?.Explode ?? Explode;
AllowReserved = header?.AllowReserved ?? AllowReserved;
_schema = JsonNodeCloneHelper.CloneJsonSchema(header?.Schema);
Example = JsonNodeCloneHelper.Clone(header?.Example);
Schema = header?.Schema != null ? JsonNodeCloneHelper.CloneJsonSchema(header?.Schema) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
Example = header?.Example != null ? JsonNodeCloneHelper.Clone(header?.Example) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
Examples = header?.Examples != null ? new Dictionary<string, OpenApiExample>(header.Examples) : null;
Content = header?.Content != null ? new Dictionary<string, OpenApiMediaType>(header.Content) : null;
Extensions = header?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(header.Extensions) : null;
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@
Style = parameter?.Style ?? Style;
Explode = parameter?.Explode ?? Explode;
AllowReserved = parameter?.AllowReserved ?? AllowReserved;
_schema = JsonNodeCloneHelper.CloneJsonSchema(parameter?.Schema);
Schema = parameter?.Schema != null ? JsonNodeCloneHelper.CloneJsonSchema(parameter?.Schema) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
Examples = parameter?.Examples != null ? new Dictionary<string, OpenApiExample>(parameter.Examples) : null;
Example = JsonNodeCloneHelper.Clone(parameter?.Example);
Example = parameter?.Example != null ? JsonNodeCloneHelper.Clone(parameter?.Example) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
Content = parameter?.Content != null ? new Dictionary<string, OpenApiMediaType>(parameter.Content) : null;
Extensions = parameter?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(parameter.Extensions) : null;
AllowEmptyValue = parameter?.AllowEmptyValue ?? AllowEmptyValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,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
Expand Up @@ -24,7 +24,10 @@ private OpenApiExample Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiExample>(_reference);
return _target;
OpenApiExample resolved = new OpenApiExample(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
if (!string.IsNullOrEmpty(_summary)) resolved.Summary = _summary;
return resolved;
}
}

Expand Down Expand Up @@ -71,12 +74,12 @@ internal OpenApiExampleReference(OpenApiExample target, string referenceId)
public override string Description
{
get => string.IsNullOrEmpty(_description) ? Target.Description : _description;
set => _description = value;
set => _description = value;
}

/// <inheritdoc/>
public override string Summary
{
{
get => string.IsNullOrEmpty(_summary) ? Target.Summary : _summary;
set => _summary = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ private OpenApiHeader Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiHeader>(_reference);
return _target;
OpenApiHeader resolved = new OpenApiHeader(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down Expand Up @@ -153,7 +155,7 @@ public override void SerializeAsV2(IOpenApiWriter writer)
private void SerializeInternal(IOpenApiWriter writer,
Action<IOpenApiWriter, IOpenApiReferenceable> action)
{
Utils.CheckArgumentNull(writer);;
Utils.CheckArgumentNull(writer);
action(writer, Target);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ private OpenApiLink Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiLink>(_reference);
return _target;
OpenApiLink resolved = new OpenApiLink(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ private OpenApiParameter Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiParameter>(_reference);
return _target;
OpenApiParameter resolved = new OpenApiParameter(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ private OpenApiPathItem Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiPathItem>(_reference);
return _target;
OpenApiPathItem resolved = new OpenApiPathItem(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
if (!string.IsNullOrEmpty(_summary)) resolved.Summary = _summary;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ private OpenApiRequestBody Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiRequestBody>(_reference);
return _target;
OpenApiRequestBody resolved = new OpenApiRequestBody(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ private OpenApiResponse Target
get
{
_target ??= Reference.HostDocument?.ResolveReferenceTo<OpenApiResponse>(_reference);
return _target;
OpenApiResponse resolved = new OpenApiResponse(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ private OpenApiSecurityScheme Target
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiSecurityScheme>(_reference);
return _target;
OpenApiSecurityScheme resolved = new OpenApiSecurityScheme(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ private OpenApiTag Target
{
_target ??= Reference.HostDocument?.ResolveReferenceTo<OpenApiTag>(_reference);
_target ??= new OpenApiTag() { Name = _reference.Id };
return _target;
OpenApiTag resolved = new OpenApiTag(_target);
if (!string.IsNullOrEmpty(_description)) resolved.Description = _description;
return resolved;
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Reader.Services;
using System.Collections.Generic;
using System;

namespace Microsoft.OpenApi.Reader
{
Expand Down Expand Up @@ -94,6 +96,7 @@ public async Task<ReadResult> ReadAsync(JsonNode jsonNode,
}

SetHostDocument(document);
ResolveReferences(diagnostic, document);
}
catch (OpenApiException ex)
{
Expand Down Expand Up @@ -202,5 +205,16 @@ private void SetHostDocument(OpenApiDocument document)
{
document.SetHostDocument();
}

private void ResolveReferences(OpenApiDiagnostic diagnostic, OpenApiDocument document)
{
List<OpenApiError> errors = new();
errors.AddRange(document.ResolveJsonSchemaReferences());

foreach (var item in errors)
{
diagnostic.Errors.Add(item);
}
}
}
}
Loading
Loading