Skip to content

Commit

Permalink
Merge pull request #2031 from microsoft/fix/reference-proxy-single-copy
Browse files Browse the repository at this point in the history
fix/reference proxy single copy
  • Loading branch information
baywet authored Jan 7, 2025
2 parents aa90edf + 45d49f1 commit 1394da7
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 93 deletions.
1 change: 0 additions & 1 deletion src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT license.

using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.Interfaces
{
Expand Down
1 change: 0 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;
Expand Down
61 changes: 61 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,67 @@ public static ReadResult Parse(string input,
{
return OpenApiModelFactory.Parse(input, format, settings);
}
/// <summary>
/// Adds a component to the components object of the current document and registers it to the underlying workspace.
/// </summary>
/// <param name="componentToRegister">The component to add</param>
/// <param name="id">The id for the component</param>
/// <typeparam name="T">The type of the component</typeparam>
/// <returns>Whether the component was added to the components.</returns>
/// <exception cref="ArgumentNullException">Thrown when the component is null.</exception>
/// <exception cref="ArgumentException">Thrown when the id is null or empty.</exception>
public bool AddComponent<T>(string id, T componentToRegister)
{
Utils.CheckArgumentNull(componentToRegister);
Utils.CheckArgumentNullOrEmpty(id);
Components ??= new();
switch (componentToRegister)
{
case OpenApiSchema openApiSchema:
Components.Schemas ??= new Dictionary<string, OpenApiSchema>();
Components.Schemas.Add(id, openApiSchema);
break;
case OpenApiParameter openApiParameter:
Components.Parameters ??= new Dictionary<string, OpenApiParameter>();
Components.Parameters.Add(id, openApiParameter);
break;
case OpenApiResponse openApiResponse:
Components.Responses ??= new Dictionary<string, OpenApiResponse>();
Components.Responses.Add(id, openApiResponse);
break;
case OpenApiRequestBody openApiRequestBody:
Components.RequestBodies ??= new Dictionary<string, OpenApiRequestBody>();
Components.RequestBodies.Add(id, openApiRequestBody);
break;
case OpenApiLink openApiLink:
Components.Links ??= new Dictionary<string, OpenApiLink>();
Components.Links.Add(id, openApiLink);
break;
case OpenApiCallback openApiCallback:
Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
Components.Callbacks.Add(id, openApiCallback);
break;
case OpenApiPathItem openApiPathItem:
Components.PathItems ??= new Dictionary<string, OpenApiPathItem>();
Components.PathItems.Add(id, openApiPathItem);
break;
case OpenApiExample openApiExample:
Components.Examples ??= new Dictionary<string, OpenApiExample>();
Components.Examples.Add(id, openApiExample);
break;
case OpenApiHeader openApiHeader:
Components.Headers ??= new Dictionary<string, OpenApiHeader>();
Components.Headers.Add(id, openApiHeader);
break;
case OpenApiSecurityScheme openApiSecurityScheme:
Components.SecuritySchemes ??= new Dictionary<string, OpenApiSecurityScheme>();
Components.SecuritySchemes.Add(id, openApiSecurityScheme);
break;
default:
throw new ArgumentException($"Component type {componentToRegister!.GetType().Name} is not supported.");
}
return Workspace?.RegisterComponentForDocument(this, componentToRegister, id) ?? false;
}
}

internal class FindSchemaReferences : OpenApiVisitorBase
Expand Down
180 changes: 107 additions & 73 deletions src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ public override void SerializeAsV3(IOpenApiWriter writer)
if (!writer.GetSettings().ShouldInlineReference(_reference))
{
_reference.SerializeAsV3(writer);
return;
}
else
{
Expand All @@ -94,7 +93,6 @@ public override void SerializeAsV31(IOpenApiWriter writer)
if (!writer.GetSettings().ShouldInlineReference(_reference))
{
_reference.SerializeAsV31(writer);
return;
}
else
{
Expand All @@ -108,7 +106,6 @@ public override void SerializeAsV2(IOpenApiWriter writer)
if (!writer.GetSettings().ShouldInlineReference(_reference))
{
_reference.SerializeAsV2(writer);
return;
}
else
{
Expand All @@ -119,7 +116,7 @@ public override void SerializeAsV2(IOpenApiWriter writer)
/// <inheritdoc/>
private void SerializeInternal(IOpenApiWriter writer)
{
Utils.CheckArgumentNull(writer);;
Utils.CheckArgumentNull(writer);
writer.WriteValue(Name);
}
}
Expand Down
66 changes: 54 additions & 12 deletions src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public int ComponentsCount()
{
return _IOpenApiReferenceableRegistry.Count + _artifactsRegistry.Count;
}
private const string ComponentSegmentSeparator = "/";

/// <summary>
/// Registers a document's components into the workspace
Expand All @@ -63,89 +64,130 @@ public void RegisterComponents(OpenApiDocument document)
{
if (document?.Components == null) return;

string baseUri = document.BaseUri + OpenApiConstants.ComponentsSegment;
string baseUri = getBaseUri(document);
string location;

// Register Schema
foreach (var item in document.Components.Schemas)
{
location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + "/" + item.Key;
location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + item.Key;

RegisterComponent(location, item.Value);
}

// Register Parameters
foreach (var item in document.Components.Parameters)
{
location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Parameter.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register Responses
foreach (var item in document.Components.Responses)
{
location = baseUri + ReferenceType.Response.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Response.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register RequestBodies
foreach (var item in document.Components.RequestBodies)
{
location = baseUri + ReferenceType.RequestBody.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.RequestBody.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register Links
foreach (var item in document.Components.Links)
{
location = baseUri + ReferenceType.Link.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Link.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register Callbacks
foreach (var item in document.Components.Callbacks)
{
location = baseUri + ReferenceType.Callback.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Callback.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register PathItems
foreach (var item in document.Components.PathItems)
{
location = baseUri + ReferenceType.PathItem.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.PathItem.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register Examples
foreach (var item in document.Components.Examples)
{
location = baseUri + ReferenceType.Example.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Example.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register Headers
foreach (var item in document.Components.Headers)
{
location = baseUri + ReferenceType.Header.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.Header.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}

// Register SecuritySchemes
foreach (var item in document.Components.SecuritySchemes)
{
location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + "/" + item.Key;
location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + ComponentSegmentSeparator + item.Key;
RegisterComponent(location, item.Value);
}
}

private string getBaseUri(OpenApiDocument openApiDocument)
{
return openApiDocument.BaseUri + OpenApiConstants.ComponentsSegment;
}

/// <summary>
/// Registers a component for a document in the workspace
/// </summary>
/// <param name="openApiDocument">The document to register the component for.</param>
/// <param name="componentToRegister">The component to register.</param>
/// <param name="id">The id of the component.</param>
/// <typeparam name="T">The type of the component to register.</typeparam>
/// <returns>true if the component is successfully registered; otherwise false.</returns>
/// <exception cref="ArgumentNullException">openApiDocument is null</exception>
/// <exception cref="ArgumentNullException">componentToRegister is null</exception>
/// <exception cref="ArgumentNullException">id is null or empty</exception>
public bool RegisterComponentForDocument<T>(OpenApiDocument openApiDocument, T componentToRegister, string id)
{
Utils.CheckArgumentNull(openApiDocument);
Utils.CheckArgumentNull(componentToRegister);
Utils.CheckArgumentNullOrEmpty(id);

var baseUri = getBaseUri(openApiDocument);

var location = componentToRegister switch
{
OpenApiSchema => baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiParameter => baseUri + ReferenceType.Parameter.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiResponse => baseUri + ReferenceType.Response.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiRequestBody => baseUri + ReferenceType.RequestBody.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiLink => baseUri + ReferenceType.Link.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiCallback => baseUri + ReferenceType.Callback.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiPathItem => baseUri + ReferenceType.PathItem.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiExample => baseUri + ReferenceType.Example.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiHeader => baseUri + ReferenceType.Header.GetDisplayName() + ComponentSegmentSeparator + id,
OpenApiSecurityScheme => baseUri + ReferenceType.SecurityScheme.GetDisplayName() + ComponentSegmentSeparator + id,
_ => throw new ArgumentException($"Invalid component type {componentToRegister.GetType().Name}"),
};

return RegisterComponent(location, componentToRegister);
}

/// <summary>
/// Registers a component in the component registry.
/// </summary>
/// <param name="location"></param>
/// <param name="component"></param>
/// <returns>true if the component is successfully registered; otherwise false.</returns>
public bool RegisterComponent<T>(string location, T component)
internal bool RegisterComponent<T>(string location, T component)
{
var uri = ToLocationUrl(location);
if (component is IOpenApiReferenceable referenceable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ namespace Microsoft.OpenApi.Models
public System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiTag>? Tags { get; set; }
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.Models.OpenApiPathItem>? Webhooks { get; set; }
public Microsoft.OpenApi.Services.OpenApiWorkspace? Workspace { get; set; }
public bool AddComponent<T>(string id, T componentToRegister) { }
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public void SerializeAsV31(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
Expand Down Expand Up @@ -1528,7 +1529,7 @@ namespace Microsoft.OpenApi.Services
public int ComponentsCount() { }
public bool Contains(string location) { }
public System.Uri GetDocumentId(string key) { }
public bool RegisterComponent<T>(string location, T component) { }
public bool RegisterComponentForDocument<T>(Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, T componentToRegister, string id) { }
public void RegisterComponents(Microsoft.OpenApi.Models.OpenApiDocument document) { }
public T? ResolveReference<T>(string location) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public class OpenApiReferencableTests
{ "link1", new OpenApiLink() }
}
};
private static readonly OpenApiSchema _schemaFragment = new OpenApiSchema();
private static readonly OpenApiSecurityScheme _securitySchemeFragment = new OpenApiSecurityScheme();
private static readonly OpenApiTag _tagFragment = new OpenApiTag();

Expand Down

0 comments on commit 1394da7

Please sign in to comment.