diff --git a/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj b/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj index 7effe22e..be061bdc 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj +++ b/src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj @@ -15,23 +15,13 @@ netstandard2.0 Microsoft.OpenApi.OData true - 1.0.11 + 1.1.0-preview1 This package contains the codes you need to convert OData CSDL to Open API Document of Model. © Microsoft Corporation. All rights reserved. Microsoft OpenApi OData EDM https://github.com/Microsoft/OpenAPI.NET.OData -- Fixes reading restriction annotations for entity types defining navigation properties #220 -- Enables configuring appending bound operations on derived types #221 -- Add error ranges for OData actions when ErrorResponsesAsDefault is set to false #218 -- Fixes missing bound operations on some navigation property paths #201 -- Provides support for using success status code range 2XX #153 -- Fixes request body and response representation for ref POST and PUT operations #228 -- Adds discriminator object to complex types which have derived types #233 -- Lists all the derived types of a type in discriminator mapping #240 -- Adds @odata.type property and makes this a required property in schemas that have discriminator objects #243 -- Appends `@odata.type` property to all derived types schemas #248 -- Represent nullable references within anyOf correctly #190 +- Fixes response schemas of actions and functions that return a collection to contain the nextLink property #231 Microsoft.OpenApi.OData.Reader ..\..\tool\Microsoft.OpenApi.OData.snk diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs index bcbc883c..77cd097a 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmActionOperationHandler.cs @@ -35,6 +35,16 @@ protected override void SetRequestBody(OpenApiOperation operation) protected override void SetExtensions(OpenApiOperation operation) { operation.Extensions.Add(Constants.xMsDosOperationType, new OpenApiString("action")); + if (Context.Settings.EnablePagination && EdmOperation.ReturnType?.TypeKind() == EdmTypeKind.Collection) + { + OpenApiObject extension = new OpenApiObject + { + { "nextLinkName", new OpenApiString("@odata.nextLink")}, + { "operationName", new OpenApiString(Context.Settings.PageableOperationName)} + }; + + operation.Extensions.Add(Constants.xMsPageable, extension); + } base.SetExtensions(operation); } } diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs index 4cbf0525..c1134f8d 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/EdmFunctionOperationHandler.cs @@ -27,6 +27,16 @@ internal class EdmFunctionOperationHandler : EdmOperationOperationHandler protected override void SetExtensions(OpenApiOperation operation) { operation.Extensions.Add(Constants.xMsDosOperationType, new OpenApiString("function")); + if (Context.Settings.EnablePagination && EdmOperation.ReturnType?.TypeKind() == EdmTypeKind.Collection) + { + OpenApiObject extension = new OpenApiObject + { + { "nextLinkName", new OpenApiString("@odata.nextLink")}, + { "operationName", new OpenApiString(Context.Settings.PageableOperationName)} + }; + + operation.Extensions.Add(Constants.xMsPageable, extension); + } base.SetExtensions(operation); } } diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmActionOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmActionOperationHandlerTests.cs index ed8fd1fa..a4e93bb6 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmActionOperationHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmActionOperationHandlerTests.cs @@ -54,7 +54,7 @@ public void CreateOperationForEdmActionReturnsCorrectOperation() } [Fact] - public void CreateOperationForEdmActionReturnsCorrectOperationHierarhicalClass() + public void CreateOperationForEdmActionReturnsCorrectOperationHierarchicalClass() { // Arrange IEdmModel model = EdmModelHelper.ContractServiceModel; @@ -296,5 +296,44 @@ public void OperationRestrictionsTermWorksToCreateOperationForEdmAction(bool ena Assert.Empty(operation.Security); } } + + [Theory] + [InlineData("getMailTips", true)] // returns collection + [InlineData("getMailTips", false)] // returns collection + [InlineData("findMeetingTimes", true)] // does not return collection + public void CreateOperationForEdmActionWithCollectionReturnTypeContainsXMsPageableExtension(string actionName, bool enablePagination) + { + // Arrange + IEdmModel model = EdmModelHelper.GraphBetaModel; + OpenApiConvertSettings settings = new() + { + EnableOperationId = true, + EnablePagination = enablePagination + }; + ODataContext context = new(model, settings); + IEdmAction action = model.SchemaElements.OfType() + .First(x => x.Name == actionName && + x.FindParameter("bindingParameter").Type.Definition.ToString() == "microsoft.graph.user"); + IEdmEntityContainer container = model.SchemaElements.OfType().First(); + IEdmEntitySet users = container.FindEntitySet("users"); + IEdmEntityType user = model.SchemaElements.OfType().First(x => x.Name == "user"); + + ODataPath path = new(new ODataNavigationSourceSegment(users), + new ODataKeySegment(user), + new ODataOperationSegment(action)); + + // Act + var operation = _operationHandler.CreateOperation(context, path); + + // Assert + if (enablePagination && action.ReturnType.IsCollection()) + { + Assert.True(operation.Extensions.ContainsKey(Common.Constants.xMsPageable)); + } + else + { + Assert.False(operation.Extensions.ContainsKey(Common.Constants.xMsPageable)); + } + } } } diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs index 5edb1fd9..3542a580 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/EdmFunctionOperationHandlerTests.cs @@ -112,7 +112,7 @@ public void CreateOperationForEdmFunctionReturnsCorrectOperation(bool useHTTPSta } [Fact] - public void CreateOperationForEdmFunctionReturnsCorrectOperationHierarhicalClass() + public void CreateOperationForEdmFunctionReturnsCorrectOperationHierarchicalClass() { // Arrange IEdmModel model = EdmModelHelper.ContractServiceModel; @@ -408,5 +408,42 @@ public void OperationRestrictionsTermWorksToCreateOperationForEdmFunction(bool e Assert.Empty(operation.Security); } } + + [Theory] + [InlineData("getUserArchivedPrintJobs", true)] // returns collection + [InlineData("getUserArchivedPrintJobs", false)] // returns collection + [InlineData("managedDeviceEnrollmentAbandonmentSummary", true)] // does not return collection + public void CreateOperationForEdmFunctionWithCollectionReturnTypeContainsXMsPageableExtension(string functionName, bool enablePagination) + { + // Arrange + IEdmModel model = EdmModelHelper.GraphBetaModel; + OpenApiConvertSettings settings = new() + { + EnableOperationId = true, + EnablePagination = enablePagination + }; + ODataContext context = new(model, settings); + IEdmFunction function = model.SchemaElements.OfType() + .First(x => x.Name == functionName && + x.FindParameter("bindingParameter").Type.Definition.ToString() == "microsoft.graph.reportRoot"); + IEdmEntityContainer container = model.SchemaElements.OfType().First(); + IEdmSingleton reports = container.FindSingleton("reports"); + + ODataPath path = new(new ODataNavigationSourceSegment(reports), + new ODataOperationSegment(function)); + + // Act + var operation = _operationHandler.CreateOperation(context, path); + + // Assert + if (enablePagination && function.ReturnType.IsCollection()) + { + Assert.True(operation.Extensions.ContainsKey(Common.Constants.xMsPageable)); + } + else + { + Assert.False(operation.Extensions.ContainsKey(Common.Constants.xMsPageable)); + } + } } }