Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
32ed80b
Fix securityScheme/securityRequirement serialization
dldl-cmd Jul 24, 2024
99e4e92
Bump Verify.Xunit from 26.1.2 to 26.1.5
dependabot[bot] Jul 29, 2024
da3671e
Merge pull request #1762 from microsoft/dependabot/nuget/Verify.Xunit…
MaggieKimani1 Jul 29, 2024
dcc3816
Bump Verify.Xunit from 26.1.5 to 26.1.6
dependabot[bot] Jul 31, 2024
2250776
Merge pull request #1767 from microsoft/dependabot/nuget/Verify.Xunit…
andrueastman Aug 1, 2024
d3df64d
Merge pull request #1752 from dldl-cmd/1506_securityRequirement_secur…
MaggieKimani1 Aug 2, 2024
f63443f
Serialize example values with empty arrays for responses
MaggieKimani1 Aug 2, 2024
0b63d77
Add unit test to validate
MaggieKimani1 Aug 2, 2024
91f83f8
Update comment
MaggieKimani1 Aug 2, 2024
28142d5
Simplify by using .Where() for sequence filtering
MaggieKimani1 Aug 2, 2024
2df890a
Add support for unserializable annotations on OpenAPI document
captainsafia Aug 2, 2024
38ba98e
Update src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
MaggieKimani1 Aug 5, 2024
1c19986
Update src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
MaggieKimani1 Aug 5, 2024
27258e9
Update src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
MaggieKimani1 Aug 5, 2024
d96749f
Merge pull request #1768 from microsoft/mk/fix-empty-example-array
MaggieKimani1 Aug 5, 2024
7876018
Merge pull request #1769 from captainsafia/property-bag-support
MaggieKimani1 Aug 5, 2024
0e95ced
Bump lib versions
MaggieKimani1 Aug 6, 2024
60258b5
Merge pull request #1770 from microsoft/mk/update-lib-versions
andrueastman Aug 6, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.6.16</Version>
<Version>1.6.17</Version>
<Description>OpenAPI.NET Readers for JSON and YAML documents</Description>
<SignAssembly>true</SignAssembly>
<!-- https://github.com/dotnet/sourcelink/blob/main/docs/README.md#embeduntrackedsources -->
Expand Down
19 changes: 19 additions & 0 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;

namespace Microsoft.OpenApi.Interfaces
{
/// <summary>
/// Represents an Open API element that can be annotated with
/// non-serializable properties in a property bag.
/// </summary>
public interface IOpenApiAnnotatable
{
/// <summary>
/// A collection of properties associated with the current OpenAPI element.
/// </summary>
IDictionary<string, object> Annotations { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>Latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.6.16</Version>
<Version>1.6.17</Version>
<Description>.NET models with JSON and YAML writers for OpenAPI specification</Description>
<SignAssembly>true</SignAssembly>
<!-- https://github.com/dotnet/sourcelink/blob/main/docs/README.md#embeduntrackedsources -->
Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/// <summary>
/// Describes an OpenAPI object (OpenAPI document). See: https://swagger.io/specification
/// </summary>
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
{
/// <summary>
/// Related workspace containing OpenApiDocuments that are referenced in this document
Expand Down Expand Up @@ -70,6 +70,9 @@
/// </summary>
public string HashCode => GenerateHashValue(this);

/// <inheritdoc />
public IDictionary<string, object> Annotations { get; set; }

/// <summary>
/// Parameter-less constructor
/// </summary>
Expand All @@ -89,6 +92,7 @@
Tags = document?.Tags != null ? new List<OpenApiTag>(document.Tags) : null;
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
Extensions = document?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(document.Extensions) : null;
Annotations = document?.Annotations != null ? new Dictionary<string, object>(document.Annotations) : null;
}

/// <summary>
Expand Down Expand Up @@ -437,7 +441,7 @@
return null;
}

// Todo: Verify if we need to check to see if this external reference is actually targeted at this document.

Check warning on line 444 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
if (useExternal)
{
if (this.Workspace == null)
Expand Down
31 changes: 30 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
Expand Down Expand Up @@ -76,7 +77,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.Example, Example, (w, e) => w.WriteAny(e));

// examples
writer.WriteOptionalMap(OpenApiConstants.Examples, Examples, (w, e) => e.SerializeAsV3(w));
SerializeExamples(writer, Examples);

// encoding
writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, (w, e) => e.SerializeAsV3(w));
Expand All @@ -94,5 +95,33 @@ public void SerializeAsV2(IOpenApiWriter writer)
{
// Media type does not exist in V2.
}

private static void SerializeExamples(IOpenApiWriter writer, IDictionary<string, OpenApiExample> examples)
{
/* Special case for writing out empty arrays as valid response examples
* Check if there is any example with an empty array as its value and set the flag `hasEmptyArray` to true
* */
var hasEmptyArray = examples.Values.Any( static example =>
example.Value is OpenApiArray arr && arr.Count == 0
);

if (hasEmptyArray)
{
writer.WritePropertyName(OpenApiConstants.Examples);
writer.WriteStartObject();
foreach (var kvp in examples.Where(static kvp => kvp.Value.Value is OpenApiArray arr && arr.Count == 0))
{
writer.WritePropertyName(kvp.Key);
writer.WriteStartObject();
writer.WriteRequiredObject(OpenApiConstants.Value, kvp.Value.Value, (w, v) => w.WriteAny(v));
writer.WriteEndObject();
}
writer.WriteEndObject();
}
else
{
writer.WriteOptionalMap(OpenApiConstants.Examples, examples, (w, e) => e.SerializeAsV3(w));
}
}
}
}
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
/// <summary>
/// Operation Object.
/// </summary>
public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
{
/// <summary>
/// Default value for <see cref="Deprecated"/>.
Expand Down Expand Up @@ -105,6 +105,9 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
/// </summary>
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <inheritdoc />
public IDictionary<string, object> Annotations { get; set; }

/// <summary>
/// Parameterless constructor
/// </summary>
Expand All @@ -128,6 +131,7 @@ public OpenApiOperation(OpenApiOperation operation)
Security = operation?.Security != null ? new List<OpenApiSecurityRequirement>(operation.Security) : null;
Servers = operation?.Servers != null ? new List<OpenApiServer>(operation.Servers) : null;
Extensions = operation?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(operation.Extensions) : null;
Annotations = operation?.Annotations != null ? new Dictionary<string, object>(operation.Annotations) : null;
}

/// <summary>
Expand Down
7 changes: 0 additions & 7 deletions src/Microsoft.OpenApi/Models/OpenApiReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,6 @@ public void SerializeAsV3(IOpenApiWriter writer)
return;
}

if (Type == ReferenceType.SecurityScheme)
{
// Write the string as property name
writer.WritePropertyName(ReferenceV3);
return;
}

writer.WriteStartObject();

// $ref
Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
/// <summary>
/// Schema Object.
/// </summary>
public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, IOpenApiExtensible
public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, IOpenApiExtensible, IOpenApiAnnotatable
{
/// <summary>
/// Follow JSON Schema definition. Short text providing information about the data.
Expand Down Expand Up @@ -241,6 +241,9 @@ public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, I
/// </summary>
public OpenApiReference Reference { get; set; }

/// <inheritdoc />
public IDictionary<string, object> Annotations { get; set; }

/// <summary>
/// Parameterless constructor
/// </summary>
Expand Down Expand Up @@ -290,6 +293,7 @@ public OpenApiSchema(OpenApiSchema schema)
Extensions = schema?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(schema.Extensions) : null;
UnresolvedReference = schema?.UnresolvedReference ?? UnresolvedReference;
Reference = schema?.Reference != null ? new(schema?.Reference) : null;
Annotations = schema?.Annotations != null ? new Dictionary<string, object>(schema?.Annotations) : null;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
continue;
}

securityScheme.SerializeAsV3(writer);
writer.WritePropertyName(securityScheme.Reference.ReferenceV3);

writer.WriteStartArray();

Expand Down Expand Up @@ -106,7 +106,7 @@
/// Comparer for OpenApiSecurityScheme that only considers the Id in the Reference
/// (i.e. the string that will actually be displayed in the written document)
/// </summary>
private class OpenApiSecuritySchemeReferenceEqualityComparer : IEqualityComparer<OpenApiSecurityScheme>

Check warning on line 109 in src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs

View workflow job for this annotation

GitHub Actions / Build

Private classes which are not derived in the current assembly should be marked as 'sealed'. (https://rules.sonarsource.com/csharp/RSPEC-3260)
{
/// <summary>
/// Determines whether the specified objects are equal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
using System.IO;
using FluentAssertions;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Readers.V3;
using Microsoft.OpenApi.Tests;
using Xunit;

namespace Microsoft.OpenApi.Readers.Tests.V3Tests
Expand Down Expand Up @@ -77,5 +79,43 @@ public void ParseMediaTypeWithExamplesShouldSucceed()
}
});
}

[Fact]
public void ParseMediaTypeWithEmptyArrayInExamplesWorks()
{
// Arrange
var expected = @"{
""schema"": {
""type"": ""array"",
""items"": {
""type"": ""object"",
""properties"": {
""id"": {
""type"": ""string""
}
}
}
},
""examples"": {
""Success response - no results"": {
""value"": [ ]
}
}
}
";
MapNode node;
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "examplesWithEmptyArray.json")))
{
node = TestHelper.CreateYamlMapNode(stream);
}

// Act
var mediaType = OpenApiV3Deserializer.LoadMediaType(node);
var serialized = mediaType.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);

// Assert
serialized.MakeLineBreaksEnvironmentNeutral()
.Should().BeEquivalentTo(expected.MakeLineBreaksEnvironmentNeutral());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
}
},
"examples": {
"Success response - no results": {
"value": []
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpYaml" Version="2.1.1" />
<PackageReference Include="Verify.Xunit" Version="26.1.2" />
<PackageReference Include="Verify.Xunit" Version="26.1.6" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
Expand Down
37 changes: 34 additions & 3 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public class OpenApiDocumentTests
{
Type = ReferenceType.Schema,
Id = "schema2"
}
},
Annotations = new Dictionary<string, object> { { "x-foo", "bar" } }
},
["schema2"] = new()
{
Expand All @@ -38,7 +39,8 @@ public class OpenApiDocumentTests
{
["property1"] = new()
{
Type = "string"
Type = "string",
Annotations = new Dictionary<string, object> { { "key1", "value" } }
}
}
},
Expand All @@ -56,9 +58,11 @@ public class OpenApiDocumentTests
{
["property1"] = new()
{
Type = "string"
Type = "string",
Annotations = new Dictionary<string, object> { { "key1", "value" } }
}
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Reference = new()
{
Type = ReferenceType.Schema,
Expand Down Expand Up @@ -100,6 +104,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = TopLevelReferencingComponents
};

Expand All @@ -109,6 +114,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = TopLevelSelfReferencingComponentsWithOtherProperties
};

Expand All @@ -118,6 +124,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = TopLevelSelfReferencingComponents
};

Expand Down Expand Up @@ -509,6 +516,7 @@ public class OpenApiDocumentTests
}
}
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = AdvancedComponentsWithReference
};

Expand Down Expand Up @@ -884,6 +892,7 @@ public class OpenApiDocumentTests
}
}
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = AdvancedComponents
};

Expand Down Expand Up @@ -1272,6 +1281,7 @@ public class OpenApiDocumentTests
}
}
},
Annotations = new Dictionary<string, object> { { "key1", "value" } },
Components = AdvancedComponents
};

Expand Down Expand Up @@ -1827,5 +1837,26 @@ public void SerializeV2DocumentWithStyleAsNullDoesNotWriteOutStyleValue()
expected = expected.MakeLineBreaksEnvironmentNeutral();
actual.Should().Be(expected);
}

[Fact]
public void OpenApiDocumentCopyConstructorWithAnnotationsSucceeds()
{
var baseDocument = new OpenApiDocument
{
Annotations = new Dictionary<string, object>
{
["key1"] = "value1",
["key2"] = 2
}
};

var actualDocument = new OpenApiDocument(baseDocument);

Assert.Equal(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);

baseDocument.Annotations["key1"] = "value2";

Assert.NotEqual(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);
}
}
}
Loading
Loading