Skip to content

fix: implementation drift between the different version services #2398

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

Merged
merged 1 commit into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions src/Microsoft.OpenApi/Reader/BaseOpenApiVersionService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.OpenApi.Reader;

internal abstract class BaseOpenApiVersionService : IOpenApiVersionService
{
public OpenApiDiagnostic Diagnostic { get; }

protected BaseOpenApiVersionService(OpenApiDiagnostic diagnostic)
{
Diagnostic = diagnostic;
}

internal abstract Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> Loaders { get; }

public abstract OpenApiDocument LoadDocument(RootNode rootNode, Uri location);

public T? LoadElement<T>(ParseNode node, OpenApiDocument doc) where T : IOpenApiElement
{
if (Loaders.TryGetValue(typeof(T), out var loader) && loader(node, doc) is T result)
{
return result;
}
return default;
}
public virtual string? GetReferenceScalarValues(MapNode mapNode, string scalarValue)
{
if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase)) &&
mapNode
.Where(x => x.Name.Equals(scalarValue))
.Select(static x => x.Value)
.OfType<ValueNode>().FirstOrDefault() is {} valueNode)
{
return valueNode.GetScalarValue();
}

return null;
}
}
22 changes: 5 additions & 17 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiV2VersionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ namespace Microsoft.OpenApi.Reader.V2
/// <summary>
/// The version specific implementations for OpenAPI V2.0.
/// </summary>
internal class OpenApiV2VersionService : IOpenApiVersionService
internal class OpenApiV2VersionService : BaseOpenApiVersionService
{
public OpenApiDiagnostic Diagnostic { get; }

/// <summary>
/// Create Parsing Context
/// </summary>
/// <param name="diagnostic">Provide instance for diagnostic object for collecting and accessing information about the parsing.</param>
public OpenApiV2VersionService(OpenApiDiagnostic diagnostic)
public OpenApiV2VersionService(OpenApiDiagnostic diagnostic): base(diagnostic)
{
Diagnostic = diagnostic;
}

private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> _loaders = new()
Expand All @@ -44,22 +41,13 @@ public OpenApiV2VersionService(OpenApiDiagnostic diagnostic)
[typeof(OpenApiXml)] = OpenApiV2Deserializer.LoadXml
};

public OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
public override OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
{
return OpenApiV2Deserializer.LoadOpenApi(rootNode, location);
}
internal override Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> Loaders => _loaders;

public T? LoadElement<T>(ParseNode node, OpenApiDocument doc) where T : IOpenApiElement
{
if (_loaders.TryGetValue(typeof(T), out var loader) && loader(node, doc) is T result)
{
return result;
}
return default;
}

/// <inheritdoc />
public string GetReferenceScalarValues(MapNode mapNode, string scalarValue)
public override string GetReferenceScalarValues(MapNode mapNode, string scalarValue)
{
throw new InvalidOperationException();
}
Expand Down
35 changes: 6 additions & 29 deletions src/Microsoft.OpenApi/Reader/V3/OpenApiV3VersionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,24 @@

using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.OpenApi.Reader.V3
{
/// <summary>
/// The version service for the Open API V3.0.
/// </summary>
internal class OpenApiV3VersionService : IOpenApiVersionService
internal class OpenApiV3VersionService : BaseOpenApiVersionService
{
public OpenApiDiagnostic Diagnostic { get; }

private static readonly char[] _pathSeparator = new char[] { '/' };

/// <summary>
/// Create Parsing Context
/// </summary>
/// <param name="diagnostic">Provide instance for diagnostic object for collecting and accessing information about the parsing.</param>
public OpenApiV3VersionService(OpenApiDiagnostic diagnostic)
public OpenApiV3VersionService(OpenApiDiagnostic diagnostic):base(diagnostic)
{
Diagnostic = diagnostic;
}

private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object>> _loaders = new()
private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> _loaders = new()
{
[typeof(JsonNodeExtension)] = OpenApiV3Deserializer.LoadAny,
[typeof(OpenApiCallback)] = OpenApiV3Deserializer.LoadCallback,
Expand Down Expand Up @@ -59,29 +54,11 @@ public OpenApiV3VersionService(OpenApiDiagnostic diagnostic)
[typeof(OpenApiSchemaReference)] = OpenApiV3Deserializer.LoadMapping
};

public OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
{
return OpenApiV3Deserializer.LoadOpenApi(rootNode, location);
}
internal override Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> Loaders => _loaders;

public T LoadElement<T>(ParseNode node, OpenApiDocument doc) where T : IOpenApiElement
public override OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
{
return (T)_loaders[typeof(T)](node, doc);
return OpenApiV3Deserializer.LoadOpenApi(rootNode, location);
}

/// <inheritdoc />
public string? GetReferenceScalarValues(MapNode mapNode, string scalarValue)
{
if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase)) &&
mapNode
.Where(x => x.Name.Equals(scalarValue))
.Select(static x => x.Value)
.OfType<ValueNode>().FirstOrDefault() is {} valueNode)
{
return valueNode.GetScalarValue();
}

return null;
}
}
}
33 changes: 6 additions & 27 deletions src/Microsoft.OpenApi/Reader/V31/OpenApiV31VersionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,30 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Reader.V3;

namespace Microsoft.OpenApi.Reader.V31
{
/// <summary>
/// The version service for the Open API V3.1.
/// </summary>
internal class OpenApiV31VersionService : IOpenApiVersionService
internal class OpenApiV31VersionService : BaseOpenApiVersionService
{
public OpenApiDiagnostic Diagnostic { get; }

/// <summary>
/// Create Parsing Context
/// </summary>
/// <param name="diagnostic">Provide instance for diagnotic object for collecting and accessing information about the parsing.</param>
public OpenApiV31VersionService(OpenApiDiagnostic diagnostic)
public OpenApiV31VersionService(OpenApiDiagnostic diagnostic):base(diagnostic)
{
Diagnostic = diagnostic;
}

private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object>> _loaders = new Dictionary<Type, Func<ParseNode, OpenApiDocument, object>>
private readonly Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> _loaders = new Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>>
{
[typeof(JsonNodeExtension)] = OpenApiV31Deserializer.LoadAny,
[typeof(OpenApiCallback)] = OpenApiV31Deserializer.LoadCallback,
[typeof(OpenApiComponents)] = OpenApiV31Deserializer.LoadComponents,
[typeof(OpenApiContact)] = OpenApiV31Deserializer.LoadContact,
[typeof(OpenApiDiscriminator)] = OpenApiV3Deserializer.LoadDiscriminator,
[typeof(OpenApiDiscriminator)] = OpenApiV31Deserializer.LoadDiscriminator,
[typeof(OpenApiEncoding)] = OpenApiV31Deserializer.LoadEncoding,
[typeof(OpenApiExample)] = OpenApiV31Deserializer.LoadExample,
[typeof(OpenApiExternalDocs)] = OpenApiV31Deserializer.LoadExternalDocs,
Expand Down Expand Up @@ -58,28 +54,11 @@ public OpenApiV31VersionService(OpenApiDiagnostic diagnostic)
[typeof(OpenApiSchemaReference)] = OpenApiV31Deserializer.LoadMapping
};

public OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
public override OpenApiDocument LoadDocument(RootNode rootNode, Uri location)
{
return OpenApiV31Deserializer.LoadOpenApi(rootNode, location);
}
internal override Dictionary<Type, Func<ParseNode, OpenApiDocument, object?>> Loaders => _loaders;

public T LoadElement<T>(ParseNode node, OpenApiDocument doc) where T : IOpenApiElement
{
return (T)_loaders[typeof(T)](node, doc);
}

/// <inheritdoc />
public string? GetReferenceScalarValues(MapNode mapNode, string scalarValue)
{
if (mapNode.Any(static x => !"$ref".Equals(x.Name, StringComparison.OrdinalIgnoreCase)))
{
var valueNode = mapNode.Where(x => x.Name.Equals(scalarValue))
.Select(static x => x.Value).OfType<ValueNode>().FirstOrDefault();

return valueNode?.GetScalarValue();
}

return null;
}
}
}
Loading