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());
+ }
}
}