diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs index 3c38d8b9a..604d1ccbb 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiLicenseDeserializer.cs @@ -22,6 +22,12 @@ internal static partial class OpenApiV3Deserializer o.Name = n.GetScalarValue(); } }, + { + "identifier", (o, n) => + { + o.Identifier = n.GetScalarValue(); + } + }, { "url", (o, n) => { diff --git a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs index 553844764..d591214e4 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs @@ -120,6 +120,11 @@ public static class OpenApiConstants /// public const string Name = "name"; + /// + /// Field: Identifier + /// + public const string Identifier = "identifier"; + /// /// Field: Namespace /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs index 1a8d1a4d8..f812b5b65 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs @@ -19,6 +19,11 @@ public class OpenApiLicense : IOpenApiSerializable, IOpenApiExtensible /// public string Name { get; set; } + /// + /// An SPDX license expression for the API. The identifier field is mutually exclusive of the url field. + /// + public string Identifier { get; set; } + /// /// The URL pointing to the contact information. MUST be in the format of a URL. /// @@ -40,6 +45,7 @@ public OpenApiLicense() {} public OpenApiLicense(OpenApiLicense license) { Name = license?.Name ?? Name; + Identifier = license?.Identifier ?? Identifier; Url = license?.Url != null ? new Uri(license.Url.OriginalString) : null; Extensions = license?.Extensions != null ? new Dictionary(license.Extensions) : null; } @@ -72,6 +78,9 @@ private void WriteInternal(IOpenApiWriter writer, OpenApiSpecVersion specVersion // name writer.WriteProperty(OpenApiConstants.Name, Name); + // identifier + writer.WriteProperty(OpenApiConstants.Identifier, Identifier); + // url writer.WriteProperty(OpenApiConstants.Url, Url?.OriginalString); diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index 1579f85e5..dc06db49c 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 false @@ -164,6 +164,9 @@ Never + + Never + Never diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiLicenseTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiLicenseTests.cs new file mode 100644 index 000000000..e68eab7a4 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiLicenseTests.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers.ParseNodes; +using Microsoft.OpenApi.Readers.V3; +using SharpYaml.Serialization; +using System.IO; +using Xunit; +using System.Linq; +using FluentAssertions; + +namespace Microsoft.OpenApi.Readers.Tests.V3Tests +{ + + public class OpenApiLicenseTests + { + private const string SampleFolderPath = "V3Tests/Samples/OpenApiLicense/"; + + [Fact] + public void ParseLicenseWithSpdxIdentifierShouldSucceed() + { + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "licenseWithSpdxIdentifier.yaml")); + var yamlStream = new YamlStream(); + yamlStream.Load(new StreamReader(stream)); + var yamlNode = yamlStream.Documents.First().RootNode; + + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); + + var node = new MapNode(context, (YamlMappingNode)yamlNode); + + // Act + var license = OpenApiV3Deserializer.LoadLicense(node); + + // Assert + license.Should().BeEquivalentTo( + new OpenApiLicense + { + Name = "Apache 2.0", + Identifier = "Apache-2.0" + }); + } + } +} diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiLicense/licenseWithSpdxIdentifier.yaml b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiLicense/licenseWithSpdxIdentifier.yaml new file mode 100644 index 000000000..623529f4d --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiLicense/licenseWithSpdxIdentifier.yaml @@ -0,0 +1,2 @@ +name: Apache 2.0 +identifier: Apache-2.0 diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLicenseTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiLicenseTests.cs index 46717ecec..3f5ef03b6 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLicenseTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLicenseTests.cs @@ -30,6 +30,12 @@ public class OpenApiLicenseTests } }; + public static OpenApiLicense LicenseWithIdentifier = new OpenApiLicense + { + Name = "Apache 2.0", + Identifier = "Apache-2.0" + }; + [Theory] [InlineData(OpenApiSpecVersion.OpenApi3_0)] [InlineData(OpenApiSpecVersion.OpenApi2_0)] @@ -123,5 +129,36 @@ public void ShouldCopyFromOriginalObjectWithoutMutating() Assert.NotEqual(AdvanceLicense.Name, licenseCopy.Name); Assert.NotEqual(AdvanceLicense.Url, licenseCopy.Url); } + + [Fact] + public void SerializeLicenseWithIdentifierAsJsonWorks() + { + // Arrange + var expected = + @"{ + ""name"": ""Apache 2.0"", + ""identifier"": ""Apache-2.0"" +}"; + + // Act + var actual = LicenseWithIdentifier.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0); + + // Assert + Assert.Equal(expected.MakeLineBreaksEnvironmentNeutral(), actual.MakeLineBreaksEnvironmentNeutral()); + } + + [Fact] + public void SerializeLicenseWithIdentifierAsYamlWorks() + { + // Arrange + var expected = @"name: Apache 2.0 +identifier: Apache-2.0"; + + // Act + var actual = LicenseWithIdentifier.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0); + + // Assert + Assert.Equal(expected.MakeLineBreaksEnvironmentNeutral(), actual.MakeLineBreaksEnvironmentNeutral()); + } } }