Skip to content

Commit

Permalink
Merge pull request #417 from microsoft/andrueastman/ms-flags
Browse files Browse the repository at this point in the history
Pass Bitwise Enum information
  • Loading branch information
andrueastman authored Sep 1, 2023
2 parents 35362bb + 5502b35 commit ef7d578
Show file tree
Hide file tree
Showing 29 changed files with 207 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,18 @@ public static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEdm
// whose value is the value of the unqualified annotation Core.Description of the enumeration type.
Description = context.Model.GetDescriptionAnnotation(enumType)
};

// If the enum is flagged, add the extension info to the description
if (context.Settings.AddEnumFlagsExtension && enumType.IsFlags)
{
var enumFlagsExtension = new OpenApiEnumFlagsExtension
{
IsFlags = true,
Style = "simple"
};
schema.Extensions.Add(enumFlagsExtension.Name, enumFlagsExtension);
}

var extension = (context.Settings.OpenApiSpecVersion == OpenApiSpecVersion.OpenApi2_0 ||
context.Settings.OpenApiSpecVersion == OpenApiSpecVersion.OpenApi3_0 ) &&
context.Settings.AddEnumDescriptionExtension ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<PackageId>Microsoft.OpenApi.OData</PackageId>
<SignAssembly>true</SignAssembly>
<Version>1.5.0-preview2</Version>
<Version>1.5.0-preview3</Version>
<Description>This package contains the codes you need to convert OData CSDL to Open API Document of Model.</Description>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>Microsoft OpenApi OData EDM</PackageTags>
Expand All @@ -24,6 +24,7 @@
- Resolves operation ids for $count and overloaded functions paths #382, #383
- Updates README #13, #253, #40
- Fixes casing in default propertyName for `innerError` in the `ErrorMainSchema`
- Adds support for `x-ms-enum-flags` extension for flagged enums
</PackageReleaseNotes>
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
Expand Down
6 changes: 6 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/OpenApiConvertSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ public string PathPrefix
/// </summary>
public bool AddEnumDescriptionExtension { get; set; } = false;

/// <summary>
/// Gets/sets a value indicating whether or not to add a "x-ms-enum-flags" extension to the enum type schema.
/// </summary>
public bool AddEnumFlagsExtension { get; set; } = true;

/// <summary>
/// Gets/sets a value indicating whether the error responses should be described as a default response or as 4XX and 5XX error responses.
/// </summary>
Expand Down Expand Up @@ -377,6 +382,7 @@ internal OpenApiConvertSettings Clone()
RequireDerivedTypesConstraintForODataTypeCastSegments = this.RequireDerivedTypesConstraintForODataTypeCastSegments,
EnableDeprecationInformation = this.EnableDeprecationInformation,
AddEnumDescriptionExtension = this.AddEnumDescriptionExtension,
AddEnumFlagsExtension = this.AddEnumFlagsExtension,
ErrorResponsesAsDefault = this.ErrorResponsesAsDefault,
InnerErrorComplexTypeName = this.InnerErrorComplexTypeName,
RequireRestrictionAnnotationsToGenerateComplexPropertyPaths = this.RequireRestrictionAnnotationsToGenerateComplexPropertyPaths,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------

using System;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.OData.OpenApiExtensions;

/// <summary>
/// Extension element for OpenAPI to add deprecation information. x-ms-enum-flags
/// </summary>
public class OpenApiEnumFlagsExtension : IOpenApiExtension
{
/// <summary>
/// Name of the extension as used in the description.
/// </summary>
public string Name => "x-ms-enum-flags";
/// <summary>
/// Whether the enum is a flagged enum.
/// </summary>
public bool IsFlags { get; set; }
/// <summary>
/// The serialization style of the flagged enum.
/// </summary>
public string Style { get; set; }
/// <inheritdoc />
public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
if(writer == null)
throw new ArgumentNullException(nameof(writer));

writer.WriteStartObject();
writer.WriteProperty(nameof(IsFlags).ToFirstCharacterLowerCase(), IsFlags);
writer.WriteProperty(nameof(Style).ToFirstCharacterLowerCase(),Style);
writer.WriteEndObject();
}
}
10 changes: 10 additions & 0 deletions src/Microsoft.OpenApi.OData.Reader/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Microsoft.OpenApi.OData.Edm.ODataSegment.GetPathItemName(Microsoft.OpenApi.OData
Microsoft.OpenApi.OData.Edm.ODataTypeCastSegment.ODataTypeCastSegment(Microsoft.OData.Edm.IEdmStructuredType structuredType, Microsoft.OData.Edm.IEdmModel model) -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.AddAlternateKeyPaths.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.AddAlternateKeyPaths.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumFlagsExtension.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.AddEnumFlagsExtension.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.AppendBoundOperationsOnDerivedTypeCastSegments.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.AppendBoundOperationsOnDerivedTypeCastSegments.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.CustomHttpMethodLinkRelMapping.get -> System.Collections.Generic.Dictionary<Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey, string>
Expand Down Expand Up @@ -48,6 +50,14 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.ShowExternalDocs.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.ShowExternalDocs.set -> void
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseSuccessStatusCodeRange.get -> bool
Microsoft.OpenApi.OData.OpenApiConvertSettings.UseSuccessStatusCodeRange.set -> void
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.IsFlags.get -> bool
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.IsFlags.set -> void
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.Name.get -> string
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.OpenApiEnumFlagsExtension() -> void
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.Style.get -> string
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.Style.set -> void
Microsoft.OpenApi.OData.OpenApiExtensions.OpenApiEnumFlagsExtension.Write(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void
Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey
Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey.Action = 6 -> Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey
Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey.Create = 2 -> Microsoft.OpenApi.OData.Vocabulary.Core.LinkRelKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ public void TripServiceMetadataToOpenApiYamlWorks(OpenApiSpecVersion specVersion
// Arrange
IEdmModel model = EdmModelHelper.TripServiceModel;

#pragma warning disable CS0618 // Type or member is obsolete
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
EnableKeyAsSegment = true,
Expand All @@ -255,6 +256,7 @@ public void TripServiceMetadataToOpenApiYamlWorks(OpenApiSpecVersion specVersion
AppendBoundOperationsOnDerivedTypeCastSegments = true,
IncludeAssemblyInfo = false
};
#pragma warning restore CS0618 // Type or member is obsolete

// Act
string yaml = WriteEdmModelAsOpenApi(model, OpenApiFormat.Yaml, settings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ public void CreateKeyParametersForAlternateKeyWithSinglePropertyWorks()

// Assert
Assert.NotNull(parameters);
Assert.Equal(1, parameters.Count);
Assert.Single(parameters);
string json = altParameter.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
Assert.Equal(@"{
""name"": ""AltId"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ public void CreateComplexTypeWithBaseSchemaReturnCorrectSchema()
Assert.Null(declaredSchema.OneOf);

Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
Assert.Single(declaredSchema.Properties);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Price", property.Key);
Assert.Equal("decimal", property.Value.OneOf.FirstOrDefault(x => !string.IsNullOrEmpty(x.Format))?.Format);
Expand Down Expand Up @@ -674,7 +674,7 @@ public void CreateEntityTypeWithBaseSchemaReturnCorrectSchema()
Assert.Null(declaredSchema.OneOf);

Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
Assert.Single(declaredSchema.Properties);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Name", property.Key);
Assert.Equal("string", property.Value.Type);
Expand Down Expand Up @@ -750,7 +750,7 @@ public void CreateEntityTypeWithCrossReferenceBaseSchemaReturnCorrectSchema()
Assert.Null(declaredSchema.OneOf);

Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
Assert.Single(declaredSchema.Properties);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Extra", property.Key);
Assert.Equal("integer", property.Value.Type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------

using System.IO;
using Microsoft.OpenApi.Writers;
using Xunit;

namespace Microsoft.OpenApi.OData.OpenApiExtensions.Tests;

public class OpenApiEnumFlagsExtensionTests
{
[Fact]
public void ExtensionNameMatchesExpected()
{
// Arrange
OpenApiEnumFlagsExtension extension = new();

// Act
string name = extension.Name;
string expectedName = "x-ms-enum-flags";

// Assert
Assert.Equal(expectedName, name);
}

[Fact]
public void WritesDefaultValues()
{
// Arrange
OpenApiEnumFlagsExtension extension = new();
using TextWriter sWriter = new StringWriter();
OpenApiJsonWriter writer = new(sWriter);

// Act
extension.Write(writer, OpenApiSpecVersion.OpenApi3_0);
string result = sWriter.ToString();

// Assert
Assert.False(extension.IsFlags);
Assert.Null(extension.Style);
Assert.Contains("\"isFlags\": false", result);
Assert.DoesNotContain("\"style\"", result);
}

[Fact]
public void WritesAllDefaultValues()
{
// Arrange
OpenApiEnumFlagsExtension extension = new() {
IsFlags = true
};
using TextWriter sWriter = new StringWriter();
OpenApiJsonWriter writer = new(sWriter);

// Act
extension.Write(writer, OpenApiSpecVersion.OpenApi3_0);
string result = sWriter.ToString();

// Assert
Assert.True(extension.IsFlags);
Assert.Null(extension.Style);
Assert.Contains("\"isFlags\": true", result);
Assert.DoesNotContain("\"style\":", result);
}

[Fact]
public void WritesAllValues()
{
// Arrange
OpenApiEnumFlagsExtension extension = new() {
IsFlags = true,
Style = "simple"
};
using TextWriter sWriter = new StringWriter();
OpenApiJsonWriter writer = new(sWriter);

// Act
extension.Write(writer, OpenApiSpecVersion.OpenApi3_0);
string result = sWriter.ToString();

// Assert
Assert.True(extension.IsFlags);
Assert.NotNull(extension.Style);
Assert.Contains("\"isFlags\": true", result);
Assert.Contains("\"style\": \"simple\"", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void CreateComplexPropertyDeleteOperationReturnsCorrectOperationForSingle
Assert.Equal("Update the BillingAddress value.", patch.Description);

Assert.NotNull(patch.Parameters);
Assert.Equal(1, patch.Parameters.Count); //id
Assert.Single(patch.Parameters); //id

Assert.NotNull(patch.Responses);
Assert.Equal(2, patch.Responses.Count);
Expand Down Expand Up @@ -85,7 +85,7 @@ public void CreateComplexPropertyPatchOperationReturnsCorrectOperationForCollect
Assert.Equal("Update the BillingAddress value.", patch.Description);

Assert.NotNull(patch.Parameters);
Assert.Equal(1, patch.Parameters.Count); //id
Assert.Single(patch.Parameters); //id

Assert.NotNull(patch.Responses);
Assert.Equal(2, patch.Responses.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void CreateComplexPropertyPutOperationReturnsCorrectOperationForSingle(bo
Assert.Equal("Update the BillingAddress value.", put.Description);

Assert.NotNull(put.Parameters);
Assert.Equal(1, put.Parameters.Count); //id
Assert.Single(put.Parameters); //id

Assert.NotNull(put.Responses);
Assert.Equal(2, put.Responses.Count);
Expand Down Expand Up @@ -95,7 +95,7 @@ public void CreateComplexPropertyPutOperationReturnsCorrectOperationForCollectio
Assert.Equal("Update the BillingAddress value.", put.Description);

Assert.NotNull(put.Parameters);
Assert.Equal(1, put.Parameters.Count); //id
Assert.Single(put.Parameters); //id

Assert.NotNull(put.Responses);
Assert.Equal(2, put.Responses.Count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void CreateOperationForEdmActionReturnsCorrectOperation()
Assert.Equal("People.Actions", tag.Name);

Assert.NotNull(operation.Parameters);
Assert.Equal(1, operation.Parameters.Count);
Assert.Single(operation.Parameters);
Assert.Equal(new string[] { "UserName" }, operation.Parameters.Select(p => p.Name));

Assert.NotNull(operation.RequestBody);
Expand Down Expand Up @@ -82,7 +82,7 @@ public void CreateOperationForEdmActionReturnsCorrectOperationHierarchicalClass(
Assert.Equal($"{entitySetName}.Actions", tag.Name);

Assert.NotNull(operation.Parameters);
Assert.Equal(1, operation.Parameters.Count);
Assert.Single(operation.Parameters);
Assert.Equal(new string[] { "id" }, operation.Parameters.Select(p => p.Name));

Assert.NotNull(operation.RequestBody);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void CreateOperationForEdmFunctionReturnsCorrectOperation(bool useHTTPSta
Assert.Equal("People.Functions", tag.Name);

Assert.NotNull(operation.Parameters);
Assert.Equal(1, operation.Parameters.Count);
Assert.Single(operation.Parameters);
Assert.Equal(new string[] { "UserName" }, operation.Parameters.Select(p => p.Name));

Assert.Null(operation.RequestBody);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void CreateEntityPatchOperationReturnsCorrectOperation(bool enableOperati
Assert.Equal("Customers.Customer", tag.Name);

Assert.NotNull(patch.Parameters);
Assert.Equal(1, patch.Parameters.Count);
Assert.Single(patch.Parameters);

Assert.NotNull(patch.RequestBody);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void CreateEntityPutOperationReturnsCorrectOperation(bool enableOperation
Assert.Equal("Customers.Customer", tag.Name);

Assert.NotNull(putOperation.Parameters);
Assert.Equal(1, putOperation.Parameters.Count);
Assert.Single(putOperation.Parameters);

Assert.NotNull(putOperation.RequestBody);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ private void VerifyEntitySetPostOperation(string annotation, bool enableOperatio
{
// RequestBody
Assert.NotNull(post.RequestBody);
Assert.Equal(1, post.RequestBody.Content.Keys.Count);
Assert.Single(post.RequestBody.Content.Keys);
Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));

// Response
Assert.Equal(1, post.Responses[Constants.StatusCode201].Content.Keys.Count);
Assert.Single(post.Responses[Constants.StatusCode201].Content.Keys);
Assert.True(post.Responses[Constants.StatusCode201].Content.ContainsKey(Constants.ApplicationJsonMediaType));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ private void VerifyMediaEntityGetOperation(string annotation, bool enableOperati
Assert.True(getOperation.Responses[statusCode].Content.ContainsKey("image/jpeg"));
Assert.Equal("The logo image.", getOperation.Description);

Assert.Equal(1, getOperation2.Responses[statusCode].Content.Keys.Count);
Assert.Single(getOperation2.Responses[statusCode].Content.Keys);
Assert.True(getOperation2.Responses[statusCode].Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
}
else
{
Assert.Equal(1, getOperation.Responses[statusCode].Content.Keys.Count);
Assert.Equal(1, getOperation2.Responses[statusCode].Content.Keys.Count);
Assert.Single(getOperation.Responses[statusCode].Content.Keys);
Assert.Single(getOperation2.Responses[statusCode].Content.Keys);
Assert.True(getOperation.Responses[statusCode].Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
Assert.True(getOperation2.Responses[statusCode].Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ private void VerifyMediaEntityPutOperation(string annotation, bool enableOperati
Assert.True(putOperation.RequestBody.Content.ContainsKey("image/jpeg"));
Assert.Equal("The logo image.", putOperation.Description);

Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
Assert.Single(putOperation2.RequestBody.Content.Keys);
Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
}
else
{
Assert.Equal(1, putOperation.RequestBody.Content.Keys.Count);
Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
Assert.Single(putOperation.RequestBody.Content.Keys);
Assert.Single(putOperation2.RequestBody.Content.Keys);
Assert.True(putOperation.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
}
Expand Down
Loading

0 comments on commit ef7d578

Please sign in to comment.