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
@ -195,7 +221,7 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
Assert.NotNull(mediaType.Schema.AnyOf);
var anyOf = Assert.Single(mediaType.Schema.AnyOf);
Assert.Equal("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person", anyOf.Reference.Id);
}
}
}
[Theory]
@ -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,8 +1313,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
@ -3000,8 +3005,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
@ -4767,8 +4777,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {

View file

@ -898,8 +898,11 @@ paths:
'200':
description: Success
schema:
default: false
type: boolean
type: object
properties:
value:
default: false
type: boolean
default:
$ref: '#/responses/error'
x-ms-docs-operation-type: function
@ -2079,8 +2082,11 @@ paths:
'200':
description: Success
schema:
default: false
type: boolean
type: object
properties:
value:
default: false
type: boolean
default:
$ref: '#/responses/error'
x-ms-docs-operation-type: function
@ -3320,8 +3326,11 @@ paths:
'200':
description: Success
schema:
default: false
type: boolean
type: object
properties:
value:
default: false
type: boolean
default:
$ref: '#/responses/error'
x-ms-docs-operation-type: function

View file

@ -1483,8 +1483,13 @@
"content": {
"application/json": {
"schema": {
"type": "boolean",
"default": false
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}
@ -3366,8 +3371,13 @@
"content": {
"application/json": {
"schema": {
"type": "boolean",
"default": false
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}
@ -5351,8 +5361,13 @@
"content": {
"application/json": {
"schema": {
"type": "boolean",
"default": false
"type": "object",
"properties": {
"value": {
"type": "boolean",
"default": false
}
}
}
}
}

View file

@ -992,8 +992,11 @@ paths:
content:
application/json:
schema:
type: boolean
default: false
type: object
properties:
value:
type: boolean
default: false
default:
$ref: '#/components/responses/error'
x-ms-docs-operation-type: function
@ -2285,8 +2288,11 @@ paths:
content:
application/json:
schema:
type: boolean
default: false
type: object
properties:
value:
type: boolean
default: false
default:
$ref: '#/components/responses/error'
x-ms-docs-operation-type: function
@ -3649,8 +3655,11 @@ paths:
content:
application/json:
schema:
type: boolean
default: false
type: object
properties:
value:
type: boolean
default: false
default:
$ref: '#/components/responses/error'
x-ms-docs-operation-type: function