Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
Expand Down Expand Up @@ -72,35 +73,76 @@ public static IDictionary<string, OpenApiResponse> CreateResponses(this ODataCon
/// </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>
/// Create the <see cref="OpenApiResponses"/> for a <see cref="IEdmOperation"/>
/// </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)
{
responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse());
}
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>
Expand All @@ -109,15 +151,15 @@ public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOp
Constants.ApplicationJsonMediaType,
new OpenApiMediaType
{
Schema = context.CreateEdmTypeSchema(operation.ReturnType)
Schema = schema
}
}
}
};
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ protected override void SetResponses(OpenApiOperation 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,56 +166,7 @@ protected override void SetParameters(OpenApiOperation 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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void CreateResponseForoperationImportThrowArgumentNullContext()
ODataContext context = null;

// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operationImport: null));
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operationImport: null, path: null));
}

[Fact]
Expand All @@ -114,7 +114,19 @@ public void CreateResponseForoperationImportThrowArgumentNullOperationImport()
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]
Expand All @@ -124,7 +136,7 @@ public void CreateResponseForOperationThrowArgumentNullContext()
ODataContext context = null;

// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operation: null));
Assert.Throws<ArgumentNullException>("context", () => context.CreateResponses(operation: null, path: null));
}

[Fact]
Expand All @@ -134,7 +146,19 @@ public void CreateResponseForOperationThrowArgumentNullOperation()
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]
Expand All @@ -157,13 +181,15 @@ public void CreateResponseForEdmFunctionReturnCorrectResponses(bool isFunctionIm
{
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
Expand Down Expand Up @@ -195,7 +221,7 @@ public void CreateResponseForEdmFunctionReturnCorrectResponses(bool isFunctionIm
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]
Expand All @@ -214,13 +240,15 @@ public void CreateResponseForEdmActionReturnCorrectResponses(string actionName,
{
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1313,8 +1313,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
Expand Down Expand Up @@ -3000,8 +3005,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
Expand Down Expand Up @@ -4767,8 +4777,13 @@
"200": {
"description": "Success",
"schema": {
"default": false,
"type": "boolean"
"type": "object",
"properties": {
"value": {
"default": false,
"type": "boolean"
}
}
}
},
"default": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading