Fixes response objects for operations that return single primitive type values (#127)

* Refactor similar code into one function; add fix for responses for primitive types

* Add new param to method and add tests for this

* Update test files

* Address PR review suggestion
This commit is contained in:
Irvine Sunday 2021-11-18 21:27:16 +03:00 committed by GitHub
parent 03b6000a84
commit 226ce62303
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 160 additions and 91 deletions

View file

@ -4,6 +4,7 @@
// ------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
@ -72,13 +73,15 @@ namespace Microsoft.OpenApi.OData.Generator
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="operationImport">The Edm operation import.</param>
/// <param name="path">The OData path.</param>
/// <returns>The created <see cref="OpenApiResponses"/>.</returns>
public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperationImport operationImport)
public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperationImport operationImport, ODataPath path)
{
Utils.CheckArgumentNull(context, nameof(context));
Utils.CheckArgumentNull(operationImport, nameof(operationImport));
Utils.CheckArgumentNull(path, nameof(path));
return context.CreateResponses(operationImport.Operation);
return context.CreateResponses(operationImport.Operation, path);
}
/// <summary>
@ -86,13 +89,15 @@ namespace Microsoft.OpenApi.OData.Generator
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="operation">The Edm operation.</param>
/// <param name="path">The OData path.</param>
/// <returns>The created <see cref="OpenApiResponses"/>.</returns>
public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperation operation)
public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperation operation, ODataPath path)
{
Utils.CheckArgumentNull(context, nameof(context));
Utils.CheckArgumentNull(operation, nameof(operation));
Utils.CheckArgumentNull(path, nameof(path));
OpenApiResponses responses = new OpenApiResponses();
OpenApiResponses responses = new();
if (operation.IsAction() && operation.ReturnType == null)
{
@ -100,7 +105,44 @@ namespace Microsoft.OpenApi.OData.Generator
}
else
{
OpenApiResponse response = new OpenApiResponse
OpenApiSchema schema;
if (operation.ReturnType.IsCollection())
{
// Get the entity type of the previous segment
IEdmEntityType entityType = path.Segments.Reverse().Skip(1)?.Take(1)?.FirstOrDefault()?.EntityType;
schema = new OpenApiSchema
{
Title = entityType == null ? null : $"Collection of {entityType.Name}",
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
{
"value", context.CreateEdmTypeSchema(operation.ReturnType)
}
}
};
}
else if (operation.ReturnType.IsPrimitive())
{
// A property or operation response that is of a primitive type is represented as an object with a single name/value pair,
// whose name is value and whose value is a primitive value.
schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
{
"value", context.CreateEdmTypeSchema(operation.ReturnType)
}
}
};
}
else
{
schema = context.CreateEdmTypeSchema(operation.ReturnType);
}
OpenApiResponse response = new()
{
Description = "Success",
Content = new Dictionary<string, OpenApiMediaType>
@ -109,7 +151,7 @@ namespace Microsoft.OpenApi.OData.Generator
Constants.ApplicationJsonMediaType,
new OpenApiMediaType
{
Schema = context.CreateEdmTypeSchema(operation.ReturnType)
Schema = schema
}
}
}
@ -117,7 +159,7 @@ namespace Microsoft.OpenApi.OData.Generator
responses.Add(Constants.StatusCode200, response);
}
// both action & function has the default response.
// Both action & function have the default response.
responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());
return responses;

View file

@ -79,7 +79,7 @@ namespace Microsoft.OpenApi.OData.Operation
// describing the structure of the success response by referencing an appropriate schema
// in the global schemas. In addition, it contains a default name/value pair for
// the OData error response referencing the global responses.
operation.Responses = Context.CreateResponses(EdmOperationImport);
operation.Responses = Context.CreateResponses(EdmOperationImport, Path);
base.SetResponses(operation);
}

View file

@ -166,56 +166,7 @@ namespace Microsoft.OpenApi.OData.Operation
/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
{
if (EdmOperation.IsAction() && EdmOperation.ReturnType == null)
{
operation.Responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse());
}
else
{
OpenApiSchema schema;
if (EdmOperation.ReturnType.TypeKind() == EdmTypeKind.Collection)
{
// Get the entity type of the previous segment
IEdmEntityType entityType = Path.Segments.Reverse().Skip(1).Take(1).FirstOrDefault().EntityType;
schema = new OpenApiSchema
{
Title = $"Collection of {entityType.Name}",
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
{
"value", Context.CreateEdmTypeSchema(EdmOperation.ReturnType)
}
}
};
}
else
{
schema = Context.CreateEdmTypeSchema(EdmOperation.ReturnType);
}
// function should have a return type.
OpenApiResponse response = new OpenApiResponse
{
Description = "Success",
Content = new Dictionary<string, OpenApiMediaType>
{
{
Constants.ApplicationJsonMediaType,
new OpenApiMediaType
{
Schema = schema
}
}
}
};
operation.Responses.Add(Constants.StatusCode200, response);
}
// both action & function has the default response.
operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());
operation.Responses = Context.CreateResponses(EdmOperation, Path);
base.SetResponses(operation);
}

View file

@ -104,7 +104,7 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operationImport: null));
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operationImport: null, path: null));
}
[Fact]
@ -114,7 +114,19 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("operationImport", () => context.CreateResponses(operationImport: null));
Assert.Throws<ArgumentNullException>("operationImport", () => context.CreateResponses(operationImport: null, path: null));
}
[Fact]
public void CreateResponseForoperationImportThrowArgumentNullPath()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false));
EdmFunctionImport functionImport = new EdmFunctionImport(new EdmEntityContainer("NS", "Default"), "MyFunctionImport", function);
// Act & Assert
Assert.Throws<ArgumentNullException>("path", () => context.CreateResponses(operationImport: functionImport, path: null));
}
[Fact]
@ -124,7 +136,7 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operation: null));
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operation: null, path: null));
}
[Fact]
@ -134,7 +146,19 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("operation", () => context.CreateResponses(operation: null));
Assert.Throws<ArgumentNullException>("operation", () => context.CreateResponses(operation: null, path: null));
}
[Fact]
public void CreateResponseForOperationThrowArgumentNullPath()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false));
EdmFunctionImport functionImport = new EdmFunctionImport(new EdmEntityContainer("NS", "Default"), "MyFunctionImport", function);
// Act & Assert
Assert.Throws<ArgumentNullException>("path", () => context.CreateResponses(operation: function, path: null));
}
[Theory]
@ -157,13 +181,15 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
{
IEdmOperationImport operationImport = model.EntityContainer.OperationImports().First(o => o.Name == operationName);
Assert.NotNull(operationImport); // guard
responses = context.CreateResponses(operationImport);
ODataPath path = new ODataPath(new ODataOperationImportSegment(operationImport));
responses = context.CreateResponses(operationImport, path);
}
else
{
IEdmOperation operation = model.SchemaElements.OfType<IEdmOperation>().First(o => o.Name == operationName);
Assert.NotNull(operation); // guard
responses = context.CreateResponses(operation);
ODataPath path = new ODataPath(new ODataOperationSegment(operation));
responses = context.CreateResponses(operation, path);
}
// Assert
@ -214,13 +240,15 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
{
IEdmOperationImport operationImport = model.EntityContainer.OperationImports().First(o => o.Name == actionName);
Assert.NotNull(operationImport); // guard
responses = context.CreateResponses(operationImport);
ODataPath path = new ODataPath(new ODataOperationImportSegment(operationImport));
responses = context.CreateResponses(operationImport, path);
}
else
{
IEdmOperation operation = model.SchemaElements.OfType<IEdmOperation>().First(o => o.Name == actionName);
Assert.NotNull(operation); // guard
responses = context.CreateResponses(operation);
ODataPath path = new ODataPath(new ODataOperationSegment(operation));
responses = context.CreateResponses(operation, path);
}
// Assert

View file

@ -1313,9 +1313,14 @@
"200": {
"description": "Success",
"schema": {
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
"$ref": "#/responses/error"
@ -3000,9 +3005,14 @@
"200": {
"description": "Success",
"schema": {
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
"$ref": "#/responses/error"
@ -4767,9 +4777,14 @@
"200": {
"description": "Success",
"schema": {
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
"$ref": "#/responses/error"

View file

@ -898,6 +898,9 @@ paths:
'200':
description: Success
schema:
type: object
properties:
value:
default: false
type: boolean
default:
@ -2079,6 +2082,9 @@ paths:
'200':
description: Success
schema:
type: object
properties:
value:
default: false
type: boolean
default:
@ -3320,6 +3326,9 @@ paths:
'200':
description: Success
schema:
type: object
properties:
value:
default: false
type: boolean
default:

View file

@ -1483,11 +1483,16 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}
},
"default": {
"$ref": "#/components/responses/error"
@ -3366,11 +3371,16 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}
},
"default": {
"$ref": "#/components/responses/error"
@ -5351,11 +5361,16 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}
},
"default": {
"$ref": "#/components/responses/error"

View file

@ -992,6 +992,9 @@ paths:
content:
application/json:
schema:
type: object
properties:
value:
type: boolean
default: false
default:
@ -2285,6 +2288,9 @@ paths:
content:
application/json:
schema:
type: object
properties:
value:
type: boolean
default: false
default:
@ -3649,6 +3655,9 @@ paths:
content:
application/json:
schema:
type: object
properties:
value:
type: boolean
default: false
default: