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

fix/reference proxy single copy #2031

Merged
merged 15 commits into from
Jan 7, 2025
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 @@ -571,6 +571,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 @@ -1532,7 +1533,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
Loading