Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
161605f
Initial plan
Copilot Oct 2, 2025
31b3821
Add support for media types components in OAS 3.2.0
Copilot Oct 2, 2025
41a231e
Add comprehensive tests for media types components support
Copilot Oct 2, 2025
c1a7106
Merge branch 'feat/oai-3-2-support' into copilot/fix-1e80ce48-c500-40…
baywet Oct 2, 2025
9928a7b
chore: partial fix of the walker implementation for media type support
baywet Oct 2, 2025
3461332
Address code review feedback: use inheritdoc and StringComparer.Ordinal
Copilot Oct 2, 2025
02cf2b8
chore: use json node deep equals for comparison
baywet Oct 3, 2025
834e41f
feat: make response request body, header and parameter content refere…
baywet Oct 3, 2025
fda6621
Add media type reference tests for various usage scenarios
Copilot Oct 3, 2025
1d80375
chore: fixes is component information while walking media types
baywet Oct 3, 2025
c574203
fix: content property for header is not getting deserialized v3/3.1/3.2
baywet Oct 3, 2025
c4238b6
feat: implements media types references resolution
baywet Oct 3, 2025
c7d7753
chore: do not serialize media type components in version prior to 3.2
baywet Oct 3, 2025
a10eec7
chore: updates comment
baywet Oct 3, 2025
de83a99
Merge branch 'feat/oai-3-2-support' into copilot/fix-1e80ce48-c500-40…
baywet Oct 3, 2025
86f4e83
chore: avoid serializing media types components for anything bellow v…
baywet Oct 3, 2025
7ebed6d
chore: refreshes benchmarks
baywet Oct 3, 2025
91f51bb
test: adds unit tests for header content property deserialization
baywet Oct 3, 2025
1fc5629
Merge branch 'feat/oai-3-2-support' into copilot/fix-1e80ce48-c500-40…
baywet Oct 3, 2025
78458e8
test: adds serialization tests for 32 media type reference serialization
baywet Oct 3, 2025
6c5f86f
test: fixes media type inlining behaviour based on settings
baywet Oct 3, 2025
49f3921
feat: implements inlining of referenced media types when serializing …
baywet Oct 3, 2025
13e2ee9
chore: updates public api export
baywet Oct 3, 2025
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
57 changes: 57 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiMediaType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.Text.Json.Nodes;

namespace Microsoft.OpenApi;

/// <summary>
/// Defines the base properties for the media type object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiMediaType : IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiMediaType>, IOpenApiReferenceable
{
/// <summary>
/// The schema defining the type used for the request body.
/// </summary>
public IOpenApiSchema? Schema { get; }

/// <summary>
/// The schema defining the type used for the items in an array media type.
/// This property is only applicable for OAS 3.2.0 and later.
/// </summary>
public IOpenApiSchema? ItemSchema { get; }

/// <summary>
/// Example of the media type.
/// The example object SHOULD be in the correct format as specified by the media type.
/// </summary>
public JsonNode? Example { get; }

/// <summary>
/// Examples of the media type.
/// Each example object SHOULD match the media type and specified schema if present.
/// </summary>
public IDictionary<string, IOpenApiExample>? Examples { get; }

/// <summary>
/// A map between a property name and its encoding information.
/// The key, being the property name, MUST exist in the schema as a property.
/// The encoding object SHALL only apply to requestBody objects
/// when the media type is multipart or application/x-www-form-urlencoded.
/// </summary>
public IDictionary<string, OpenApiEncoding>? Encoding { get; }

/// <summary>
/// An encoding object for items in an array schema.
/// Only applies when the schema is of type array.
/// </summary>
public OpenApiEncoding? ItemEncoding { get; }

/// <summary>
/// An array of encoding objects for prefixItems in an array schema.
/// Each element corresponds to a prefixItem in the schema.
/// </summary>
public IList<OpenApiEncoding>? PrefixEncoding { get; }
}
29 changes: 29 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
/// </summary>
public IDictionary<string, IOpenApiPathItem>? PathItems { get; set; }

/// <summary>
/// An object to hold reusable <see cref="IOpenApiMediaType"/> Objects.
/// </summary>
public IDictionary<string, IOpenApiMediaType>? MediaTypes { get; set; }

/// <summary>
/// This object MAY be extended with Specification Extensions.
/// </summary>
Expand All @@ -87,6 +92,7 @@ public OpenApiComponents(OpenApiComponents? components)
Links = components?.Links != null ? new Dictionary<string, IOpenApiLink>(components.Links) : null;
Callbacks = components?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(components.Callbacks) : null;
PathItems = components?.PathItems != null ? new Dictionary<string, IOpenApiPathItem>(components.PathItems) : null;
MediaTypes = components?.MediaTypes != null ? new Dictionary<string, IOpenApiMediaType>(components.MediaTypes) : null;
Extensions = components?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(components.Extensions) : null;
}

Expand Down Expand Up @@ -314,6 +320,29 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
}
});

// mediaTypes - serialize as native field in v3.2+, as extension in earlier versions
if (MediaTypes != null)
{
var mediaTypesFieldName = version >= OpenApiSpecVersion.OpenApi3_2
? OpenApiConstants.MediaTypes
: OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.MediaTypes;

writer.WriteOptionalMap(
mediaTypesFieldName,
MediaTypes,
(w, key, component) =>
{
if (component is OpenApiMediaTypeReference reference)
{
action(w, reference);
}
else
{
callback(w, component);
}
});
}

// extensions
writer.WriteExtensions(Extensions, version);
writer.WriteEndObject();
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,11 @@ public static class OpenApiConstants
/// </summary>
public const string Callbacks = "callbacks";

/// <summary>
/// Field: MediaTypes
/// </summary>
public const string MediaTypes = "mediaTypes";

/// <summary>
/// Field: Url
/// </summary>
Expand Down
25 changes: 24 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.OpenApi
/// <summary>
/// Media Type Object.
/// </summary>
public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible
public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible, IOpenApiMediaType
{
/// <summary>
/// The schema defining the type used for the request body.
Expand Down Expand Up @@ -81,6 +81,29 @@ public OpenApiMediaType(OpenApiMediaType? mediaType)
Extensions = mediaType?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(mediaType.Extensions) : null;
}

/// <summary>
/// Initializes a copy of an <see cref="OpenApiMediaType"/> object
/// </summary>
internal OpenApiMediaType(IOpenApiMediaType mediaType)
{
Schema = mediaType?.Schema?.CreateShallowCopy();
ItemSchema = mediaType?.ItemSchema?.CreateShallowCopy();
Example = mediaType?.Example != null ? JsonNodeCloneHelper.Clone(mediaType.Example) : null;
Examples = mediaType?.Examples != null ? new Dictionary<string, IOpenApiExample>(mediaType.Examples) : null;
Encoding = mediaType?.Encoding != null ? new Dictionary<string, OpenApiEncoding>(mediaType.Encoding) : null;
ItemEncoding = mediaType?.ItemEncoding != null ? new OpenApiEncoding(mediaType.ItemEncoding) : null;
PrefixEncoding = mediaType?.PrefixEncoding != null ? new List<OpenApiEncoding>(mediaType.PrefixEncoding.Select(e => new OpenApiEncoding(e))) : null;
Extensions = mediaType?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(mediaType.Extensions) : null;
}

/// <summary>
/// Creates a shallow copy of this <see cref="OpenApiMediaType"/> object
/// </summary>
public IOpenApiMediaType CreateShallowCopy()
{
return new OpenApiMediaType(this);
}

/// <summary>
/// Serialize <see cref="OpenApiMediaType"/> to Open Api v3.2.
/// </summary>
Expand Down
7 changes: 6 additions & 1 deletion src/Microsoft.OpenApi/Models/ReferenceType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public enum ReferenceType
/// <summary>
/// Path item.
/// </summary>
[Display("pathItems")] PathItem
[Display("pathItems")] PathItem,

/// <summary>
/// MediaTypes item.
/// </summary>
[Display("mediaTypes")] MediaType
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.Text.Json.Nodes;

namespace Microsoft.OpenApi
{
/// <summary>
/// Media Type Object Reference.
/// </summary>
public class OpenApiMediaTypeReference : BaseOpenApiReferenceHolder<OpenApiMediaType, IOpenApiMediaType, BaseOpenApiReference>, IOpenApiMediaType
{
/// <summary>
/// Constructor initializing the reference object.
/// </summary>
/// <param name="referenceId">The reference Id.</param>
/// <param name="hostDocument">The host OpenAPI document.</param>
/// <param name="externalResource">Optional: External resource in the reference.
/// It may be:
/// 1. a absolute/relative file path, for example: ../commons/pet.json
/// 2. a Url, for example: http://localhost/pet.json
/// </param>
public OpenApiMediaTypeReference(string referenceId, OpenApiDocument? hostDocument = null, string? externalResource = null) : base(referenceId, hostDocument, ReferenceType.MediaType, externalResource)
{
}

/// <summary>
/// Copy constructor
/// </summary>
/// <param name="mediaTypeReference">The media type reference to copy</param>
private OpenApiMediaTypeReference(OpenApiMediaTypeReference mediaTypeReference) : base(mediaTypeReference)
{
}

/// <inheritdoc/>
public IOpenApiSchema? Schema { get => Target?.Schema; }

/// <inheritdoc/>
public IOpenApiSchema? ItemSchema { get => Target?.ItemSchema; }

/// <inheritdoc/>
public JsonNode? Example { get => Target?.Example; }

/// <inheritdoc/>
public IDictionary<string, IOpenApiExample>? Examples { get => Target?.Examples; }

/// <inheritdoc/>
public IDictionary<string, OpenApiEncoding>? Encoding { get => Target?.Encoding; }

/// <inheritdoc/>
public OpenApiEncoding? ItemEncoding { get => Target?.ItemEncoding; }

/// <inheritdoc/>
public IList<OpenApiEncoding>? PrefixEncoding { get => Target?.PrefixEncoding; }

/// <inheritdoc/>
public IDictionary<string, IOpenApiExtension>? Extensions { get => Target?.Extensions; }

/// <inheritdoc/>
public override IOpenApiMediaType CopyReferenceAsTargetElementWithOverrides(IOpenApiMediaType source)
{
return source is OpenApiMediaType ? new OpenApiMediaType(this) : source;
}

/// <inheritdoc/>
public IOpenApiMediaType CreateShallowCopy()
{
return new OpenApiMediaTypeReference(this);
}

/// <inheritdoc/>
protected override BaseOpenApiReference CopyReference(BaseOpenApiReference sourceReference)
{
return new BaseOpenApiReference(sourceReference);
}
}
}
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,14 @@ public virtual void Visit(OpenApiResponses response)
/// <summary>
/// Visits media type content.
/// </summary>
public virtual void Visit(IDictionary<string, OpenApiMediaType> content)
public virtual void Visit(IDictionary<string, IOpenApiMediaType> content)
{
}

/// <summary>
/// Visits <see cref="OpenApiMediaType"/>
/// Visits <see cref="IOpenApiMediaType"/>
/// </summary>
public virtual void Visit(OpenApiMediaType mediaType)
public virtual void Visit(IOpenApiMediaType mediaType)
{
}

Expand Down
Loading
Loading