Skip to content

Commit

Permalink
Merge pull request #2089 from microsoft/fix/example-reference
Browse files Browse the repository at this point in the history
fix/example reference
  • Loading branch information
baywet authored Jan 24, 2025
2 parents 2bca1df + cc28ff2 commit 079ab11
Show file tree
Hide file tree
Showing 57 changed files with 466 additions and 348 deletions.
27 changes: 16 additions & 11 deletions src/Microsoft.OpenApi/Extensions/OpenApiReferencableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Exceptions;
Expand All @@ -13,7 +14,7 @@ namespace Microsoft.OpenApi.Extensions
/// <summary>
/// Extension methods for resolving references on <see cref="IOpenApiReferenceable"/> elements.
/// </summary>
public static class OpenApiReferencableExtensions
public static class OpenApiReferenceableExtensions
{
/// <summary>
/// Resolves a JSON Pointer with respect to an element, returning the referenced element.
Expand Down Expand Up @@ -57,13 +58,15 @@ private static IOpenApiReferenceable ResolveReferenceOnHeaderElement(
string mapKey,
JsonPointer pointer)
{
switch (propertyName)
if (OpenApiConstants.Examples.Equals(propertyName, StringComparison.Ordinal) &&
!string.IsNullOrEmpty(mapKey) &&
headerElement?.Examples != null &&
headerElement.Examples.TryGetValue(mapKey, out var exampleElement) &&
exampleElement is IOpenApiReferenceable referenceable)
{
case OpenApiConstants.Examples when mapKey != null:
return headerElement.Examples[mapKey];
default:
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
return referenceable;
}
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
}

private static IOpenApiReferenceable ResolveReferenceOnParameterElement(
Expand All @@ -72,13 +75,15 @@ private static IOpenApiReferenceable ResolveReferenceOnParameterElement(
string mapKey,
JsonPointer pointer)
{
switch (propertyName)
if (OpenApiConstants.Examples.Equals(propertyName, StringComparison.Ordinal) &&
!string.IsNullOrEmpty(mapKey) &&
parameterElement?.Examples != null &&
parameterElement.Examples.TryGetValue(mapKey, out var exampleElement) &&
exampleElement is IOpenApiReferenceable referenceable)
{
case OpenApiConstants.Examples when mapKey != null:
return parameterElement.Examples[mapKey];
default:
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
return referenceable;
}
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
}

private static IOpenApiReferenceable ResolveReferenceOnResponseElement(
Expand Down
15 changes: 15 additions & 0 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiReadOnlyExtensible.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace Microsoft.OpenApi.Interfaces;

/// <summary>
/// Represents an Extensible Open API element elements can be rad from.
/// </summary>
public interface IOpenApiReadOnlyExtensible
{
/// <summary>
/// Specification extensions.
/// </summary>
IDictionary<string, IOpenApiExtension> Extensions { get; }

}
47 changes: 47 additions & 0 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Interfaces
{
/// <summary>
/// A generic interface for OpenApiReferenceable objects that have a target.
/// </summary>
/// <typeparam name="T">Type of the target being referenced</typeparam>
public interface IOpenApiReferenceHolder<out T> : IOpenApiReferenceHolder where T : IOpenApiReferenceable
{
/// <summary>
/// Gets the resolved target object.
/// </summary>
T Target { get; }
}
/// <summary>
/// A generic interface for OpenApiReferenceable objects that have a target.
/// </summary>
/// <typeparam name="T">The type of the target being referenced</typeparam>
/// <typeparam name="V">The type of the interface implemented by both the target and the reference type</typeparam>
public interface IOpenApiReferenceHolder<out T, V> : IOpenApiReferenceHolder<T> where T : IOpenApiReferenceable, V
{
//TODO merge this interface with the previous once all implementations are updated
/// <summary>
/// Copy the reference as a target element with overrides.
/// </summary>
V CopyReferenceAsTargetElementWithOverrides(V source);
}
/// <summary>
/// A generic interface for OpenApiReferenceable objects that have a target.
/// </summary>
public interface IOpenApiReferenceHolder : IOpenApiSerializable
{
/// <summary>
/// Indicates if object is populated with data or is just a reference to the data
/// </summary>
bool UnresolvedReference { get; set; }

/// <summary>
/// Reference object.
/// </summary>
OpenApiReference Reference { get; set; }
}
}
11 changes: 0 additions & 11 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Interfaces
{
/// <summary>
/// Represents an Open API element is referenceable.
/// </summary>
public interface IOpenApiReferenceable : IOpenApiSerializable
{
/// <summary>
/// Indicates if object is populated with data or is just a reference to the data
/// </summary>
bool UnresolvedReference { get; set; }

/// <summary>
/// Reference object.
/// </summary>
OpenApiReference Reference { get; set; }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Describes an element that has a summary and description.
/// </summary>
public interface IOpenApiDescribedElement : IOpenApiElement
{
/// <summary>
/// Short description for the example.
/// </summary>
public string Summary { get; set; }

/// <summary>
/// Long description for the example.
/// CommonMark syntax MAY be used for rich text representation.
/// </summary>
public string Description { get; set; }
}
26 changes: 26 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Text.Json.Nodes;
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Defines the base properties for the example object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiExample : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// Embedded literal example. The value field and externalValue field are mutually
/// exclusive. To represent examples of media types that cannot naturally represented
/// in JSON or YAML, use a string value to contain the example, escaping where necessary.
/// </summary>
public JsonNode Value { get; }

/// <summary>
/// A URL that points to the literal example.
/// This provides the capability to reference examples that cannot easily be
/// included in JSON or YAML documents.
/// The value field and externalValue field are mutually exclusive.
/// </summary>
public string ExternalValue { get; }
}
7 changes: 4 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -35,7 +36,7 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
/// <summary>
/// An object to hold reusable <see cref="OpenApiExample"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiExample>? Examples { get; set; } = new Dictionary<string, OpenApiExample>();
public virtual IDictionary<string, IOpenApiExample>? Examples { get; set; } = new Dictionary<string, IOpenApiExample>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiRequestBody"/> Objects.
Expand Down Expand Up @@ -87,7 +88,7 @@ public OpenApiComponents(OpenApiComponents? components)
Schemas = components?.Schemas != null ? new Dictionary<string, OpenApiSchema>(components.Schemas) : null;
Responses = components?.Responses != null ? new Dictionary<string, OpenApiResponse>(components.Responses) : null;
Parameters = components?.Parameters != null ? new Dictionary<string, OpenApiParameter>(components.Parameters) : null;
Examples = components?.Examples != null ? new Dictionary<string, OpenApiExample>(components.Examples) : null;
Examples = components?.Examples != null ? new Dictionary<string, IOpenApiExample>(components.Examples) : null;
RequestBodies = components?.RequestBodies != null ? new Dictionary<string, OpenApiRequestBody>(components.RequestBodies) : null;
Headers = components?.Headers != null ? new Dictionary<string, OpenApiHeader>(components.Headers) : null;
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, OpenApiSecurityScheme>(components.SecuritySchemes) : null;
Expand Down Expand Up @@ -160,7 +161,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
/// Serialize <see cref="OpenApiComponents"/>.
/// </summary>
private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
Action<IOpenApiWriter, IOpenApiSerializable> callback, Action<IOpenApiWriter, IOpenApiReferenceable> action)
Action<IOpenApiWriter, IOpenApiSerializable> callback, Action<IOpenApiWriter, IOpenApiReferenceHolder> action)
{
// Serialize each referenceable object as full object without reference if the reference in the object points to itself.
// If the reference exists but points to other objects, the object is serialized to just that reference.
Expand Down
10 changes: 6 additions & 4 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Reader;
using Microsoft.OpenApi.Services;
Expand Down Expand Up @@ -615,7 +616,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
Components.PathItems.Add(id, openApiPathItem);
break;
case OpenApiExample openApiExample:
Components.Examples ??= new Dictionary<string, OpenApiExample>();
Components.Examples ??= new Dictionary<string, IOpenApiExample>();
Components.Examples.Add(id, openApiExample);
break;
case OpenApiHeader openApiHeader:
Expand Down Expand Up @@ -645,9 +646,10 @@ public static void ResolveSchemas(OpenApiComponents? components, Dictionary<stri
walker.Walk(components);
}

public override void Visit(IOpenApiReferenceable referenceable)
/// <inheritdoc/>
public override void Visit(IOpenApiReferenceHolder referenceHolder)
{
switch (referenceable)
switch (referenceHolder)
{
case OpenApiSchema schema:
if (!Schemas.ContainsKey(schema.Reference.Id))
Expand All @@ -659,7 +661,7 @@ public override void Visit(IOpenApiReferenceable referenceable)
default:
break;
}
base.Visit(referenceable);
base.Visit(referenceHolder);
}

public override void Visit(OpenApiSchema schema)
Expand Down
Loading

0 comments on commit 079ab11

Please # to comment.