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 copy #2046

Merged
merged 10 commits into from
Jan 21, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.OpenApi.Models.References
/// </summary>
public class OpenApiSchemaReference : OpenApiSchema
{
#nullable enable
#nullable enable
private OpenApiSchema? _target;
private readonly OpenApiReference _reference;
private string? _description;
Expand Down Expand Up @@ -69,8 +69,14 @@ public class OpenApiSchemaReference : OpenApiSchema
private bool? _unevaluatedProperties;
private IList<JsonNode>? _enum;

private OpenApiSchema? Target
#nullable restore
/// <summary>
/// Gets the target schema.
/// </summary>
/// <remarks>
/// If the reference is not resolved, this will return null.
/// </remarks>
public OpenApiSchema? Target
#nullable restore
{
get
{
Expand Down Expand Up @@ -190,7 +196,7 @@ public override string Description
/// <inheritdoc/>
public override bool? UniqueItems { get => _uniqueItems is not null ? _uniqueItems : Target?.UniqueItems; set => _uniqueItems = value; }
/// <inheritdoc/>
public override IDictionary<string, OpenApiSchema> Properties { get => _properties is not null ? _properties : Target?.Properties ; set => _properties = value; }
public override IDictionary<string, OpenApiSchema> Properties { get => _properties is not null ? _properties : Target?.Properties; set => _properties = value; }
/// <inheritdoc/>
public override IDictionary<string, OpenApiSchema> PatternProperties { get => _patternProperties is not null ? _patternProperties : Target?.PatternProperties; set => _patternProperties = value; }
/// <inheritdoc/>
Expand Down Expand Up @@ -257,7 +263,7 @@ public override void SerializeAsV3(IOpenApiWriter writer)
_reference.SerializeAsV3(writer);
return;
}

SerializeInternal(writer, (writer, element) => element.SerializeAsV3(writer));
writer.GetSettings().LoopDetector.PopLoop<OpenApiSchema>();
}
Expand Down
304 changes: 151 additions & 153 deletions src/Microsoft.OpenApi/Services/CopyReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,183 +4,181 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.References;

namespace Microsoft.OpenApi.Services
namespace Microsoft.OpenApi.Services;
internal class CopyReferences(OpenApiDocument target) : OpenApiVisitorBase
{
internal class CopyReferences : OpenApiVisitorBase
private readonly OpenApiDocument _target = target;
public OpenApiComponents Components = new();

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field 'Components' can be 'readonly'.

/// <summary>
/// Visits IOpenApiReferenceable instances that are references and not in components.
/// </summary>
/// <param name="referenceable"> An IOpenApiReferenceable object.</param>
public override void Visit(IOpenApiReferenceable referenceable)
{
private readonly OpenApiDocument _target;
public OpenApiComponents Components = new();

public CopyReferences(OpenApiDocument target)
switch (referenceable)
{
_target = target;
}
case OpenApiSchemaReference openApiSchemaReference:
AddSchemaToComponents(openApiSchemaReference.Target, openApiSchemaReference.Reference.Id);
break;
case OpenApiSchema schema:
AddSchemaToComponents(schema);
break;

case OpenApiParameter parameter:
EnsureComponentsExist();
EnsureParametersExist();
if (!Components.Parameters.ContainsKey(parameter.Reference.Id))
{
Components.Parameters.Add(parameter.Reference.Id, parameter);
}
break;

/// <summary>
/// Visits IOpenApiReferenceable instances that are references and not in components.
/// </summary>
/// <param name="referenceable"> An IOpenApiReferenceable object.</param>
public override void Visit(IOpenApiReferenceable referenceable)
{
switch (referenceable)
{
case OpenApiSchema schema:
EnsureComponentsExist();
EnsureSchemasExist();
if (!Components.Schemas.ContainsKey(schema.Reference.Id))
{
Components.Schemas.Add(schema.Reference.Id, schema);
}
break;

case OpenApiParameter parameter:
EnsureComponentsExist();
EnsureParametersExist();
if (!Components.Parameters.ContainsKey(parameter.Reference.Id))
{
Components.Parameters.Add(parameter.Reference.Id, parameter);
}
break;

case OpenApiResponse response:
EnsureComponentsExist();
EnsureResponsesExist();
if (!Components.Responses.ContainsKey(response.Reference.Id))
{
Components.Responses.Add(response.Reference.Id, response);
}
break;

case OpenApiRequestBody requestBody:
EnsureComponentsExist();
EnsureResponsesExist();
EnsureRequestBodiesExist();
if (!Components.RequestBodies.ContainsKey(requestBody.Reference.Id))
{
Components.RequestBodies.Add(requestBody.Reference.Id, requestBody);
}
break;

case OpenApiExample example:
EnsureComponentsExist();
EnsureExamplesExist();
if (!Components.Examples.ContainsKey(example.Reference.Id))
{
Components.Examples.Add(example.Reference.Id, example);
}
break;

case OpenApiHeader header:
EnsureComponentsExist();
EnsureHeadersExist();
if (!Components.Headers.ContainsKey(header.Reference.Id))
{
Components.Headers.Add(header.Reference.Id, header);
}
break;

case OpenApiCallback callback:
EnsureComponentsExist();
EnsureCallbacksExist();
if (!Components.Callbacks.ContainsKey(callback.Reference.Id))
{
Components.Callbacks.Add(callback.Reference.Id, callback);
}
break;

case OpenApiLink link:
EnsureComponentsExist();
EnsureLinksExist();
if (!Components.Links.ContainsKey(link.Reference.Id))
{
Components.Links.Add(link.Reference.Id, link);
}
break;

case OpenApiSecurityScheme securityScheme:
EnsureComponentsExist();
EnsureSecuritySchemesExist();
if (!Components.SecuritySchemes.ContainsKey(securityScheme.Reference.Id))
{
Components.SecuritySchemes.Add(securityScheme.Reference.Id, securityScheme);
}
break;

default:
break;
}

base.Visit(referenceable);
}
case OpenApiResponse response:
EnsureComponentsExist();
EnsureResponsesExist();
if (!Components.Responses.ContainsKey(response.Reference.Id))
{
Components.Responses.Add(response.Reference.Id, response);
}
break;

/// <summary>
/// Visits <see cref="OpenApiSchema"/>
/// </summary>
/// <param name="schema">The OpenApiSchema to be visited.</param>
public override void Visit(OpenApiSchema schema)
{
// This is needed to handle schemas used in Responses in components
if (schema.Reference != null)
{
case OpenApiRequestBody requestBody:
EnsureComponentsExist();
EnsureSchemasExist();
if (!Components.Schemas.ContainsKey(schema.Reference.Id))
EnsureResponsesExist();
EnsureRequestBodiesExist();
if (!Components.RequestBodies.ContainsKey(requestBody.Reference.Id))
{
Components.Schemas.Add(schema.Reference.Id, schema);
Components.RequestBodies.Add(requestBody.Reference.Id, requestBody);
}
}
base.Visit(schema);
}
break;

private void EnsureComponentsExist()
{
_target.Components ??= new();
}
case OpenApiExample example:
EnsureComponentsExist();
EnsureExamplesExist();
if (!Components.Examples.ContainsKey(example.Reference.Id))
{
Components.Examples.Add(example.Reference.Id, example);
}
break;

private void EnsureSchemasExist()
{
_target.Components.Schemas ??= new Dictionary<string, OpenApiSchema>();
}
case OpenApiHeader header:
EnsureComponentsExist();
EnsureHeadersExist();
if (!Components.Headers.ContainsKey(header.Reference.Id))
{
Components.Headers.Add(header.Reference.Id, header);
}
break;

private void EnsureParametersExist()
{
_target.Components.Parameters ??= new Dictionary<string, OpenApiParameter>();
}
case OpenApiCallback callback:
EnsureComponentsExist();
EnsureCallbacksExist();
if (!Components.Callbacks.ContainsKey(callback.Reference.Id))
{
Components.Callbacks.Add(callback.Reference.Id, callback);
}
break;

private void EnsureResponsesExist()
{
_target.Components.Responses ??= new Dictionary<string, OpenApiResponse>();
}
case OpenApiLink link:
EnsureComponentsExist();
EnsureLinksExist();
if (!Components.Links.ContainsKey(link.Reference.Id))
{
Components.Links.Add(link.Reference.Id, link);
}
break;

private void EnsureRequestBodiesExist()
{
_target.Components.RequestBodies ??= new Dictionary<string, OpenApiRequestBody>();
}
case OpenApiSecurityScheme securityScheme:
EnsureComponentsExist();
EnsureSecuritySchemesExist();
if (!Components.SecuritySchemes.ContainsKey(securityScheme.Reference.Id))
{
Components.SecuritySchemes.Add(securityScheme.Reference.Id, securityScheme);
}
break;

private void EnsureExamplesExist()
{
_target.Components.Examples ??= new Dictionary<string, OpenApiExample>();
default:
break;
}

private void EnsureHeadersExist()
{
_target.Components.Headers ??= new Dictionary<string, OpenApiHeader>();
}
base.Visit(referenceable);
}

private void EnsureCallbacksExist()
private void AddSchemaToComponents(OpenApiSchema schema, string referenceId = null)
{
EnsureComponentsExist();
EnsureSchemasExist();
if (!Components.Schemas.ContainsKey(referenceId ?? schema.Reference.Id))
{
_target.Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
Components.Schemas.Add(referenceId ?? schema.Reference.Id, schema);
}
}

private void EnsureLinksExist()
/// <inheritdoc/>
public override void Visit(OpenApiSchema schema)
{
// This is needed to handle schemas used in Responses in components
if (schema is OpenApiSchemaReference openApiSchemaReference)
{
_target.Components.Links ??= new Dictionary<string, OpenApiLink>();
AddSchemaToComponents(openApiSchemaReference.Target, openApiSchemaReference.Reference.Id);
}

private void EnsureSecuritySchemesExist()
else if (schema.Reference != null)
{
_target.Components.SecuritySchemes ??= new Dictionary<string, OpenApiSecurityScheme>();
AddSchemaToComponents(schema);
}
base.Visit(schema);
}

private void EnsureComponentsExist()
{
_target.Components ??= new();
}

private void EnsureSchemasExist()
{
_target.Components.Schemas ??= new Dictionary<string, OpenApiSchema>();
}

private void EnsureParametersExist()
{
_target.Components.Parameters ??= new Dictionary<string, OpenApiParameter>();
}

private void EnsureResponsesExist()
{
_target.Components.Responses ??= new Dictionary<string, OpenApiResponse>();
}

private void EnsureRequestBodiesExist()
{
_target.Components.RequestBodies ??= new Dictionary<string, OpenApiRequestBody>();
}

private void EnsureExamplesExist()
{
_target.Components.Examples ??= new Dictionary<string, OpenApiExample>();
}

private void EnsureHeadersExist()
{
_target.Components.Headers ??= new Dictionary<string, OpenApiHeader>();
}

private void EnsureCallbacksExist()
{
_target.Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
}

private void EnsureLinksExist()
{
_target.Components.Links ??= new Dictionary<string, OpenApiLink>();
}

private void EnsureSecuritySchemesExist()
{
_target.Components.SecuritySchemes ??= new Dictionary<string, OpenApiSecurityScheme>();
}
}
5 changes: 0 additions & 5 deletions src/Microsoft.OpenApi/Writers/OpenApiWriterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,5 @@ internal bool ShouldInlineReference(OpenApiReference reference)
return (reference.IsLocal && InlineLocalReferences)
|| (reference.IsExternal && InlineExternalReferences);
}

internal bool ShouldInlineReference()
{
return InlineLocalReferences || InlineExternalReferences;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,7 @@ namespace Microsoft.OpenApi.Models.References
public class OpenApiSchemaReference : Microsoft.OpenApi.Models.OpenApiSchema
{
public OpenApiSchemaReference(string referenceId, Microsoft.OpenApi.Models.OpenApiDocument hostDocument, string externalResource = null) { }
public Microsoft.OpenApi.Models.OpenApiSchema? Target { get; }
public override Microsoft.OpenApi.Models.OpenApiSchema AdditionalProperties { get; set; }
public override bool AdditionalPropertiesAllowed { get; set; }
public override System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiSchema> AllOf { get; set; }
Expand Down
Loading