Add Metadata, segment and related path handler and operation handler, expand ODataPath to accept PathTemplate

This commit is contained in:
Sam Xu 2021-02-10 16:03:52 -08:00
parent e1763727f5
commit aa5b0f0bc7
17 changed files with 365 additions and 3 deletions

View file

@ -15,6 +15,11 @@ namespace Microsoft.OpenApi.OData.Common
/// </summary>
public static string ApplicationJsonMediaType = "application/json";
/// <summary>
/// application/xml
/// </summary>
public static string ApplicationXmlMediaType = "application/xml";
/// <summary>
/// application/octet-stream
/// </summary>

View file

@ -0,0 +1,24 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// The $count segment.
/// </summary>
public class ODataDollarCountSegment : ODataSegment
{
/// <inheritdoc />
public override ODataSegmentKind Kind => ODataSegmentKind.DollarCount;
/// <inheritdoc />
public override string Identifier => "$count";
/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings, HashSet<string> parameters) => "$count";
}
}

View file

@ -0,0 +1,24 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// The $metadata segment.
/// </summary>
public class ODataMetadataSegment : ODataSegment
{
/// <inheritdoc />
public override ODataSegmentKind Kind => ODataSegmentKind.Metadata;
/// <inheritdoc />
public override string Identifier => "$metadata";
/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings, HashSet<string> parameters) => "$metadata";
}
}

View file

@ -45,6 +45,17 @@ namespace Microsoft.OpenApi.OData.Edm
{
}
/// <summary>
/// Gets/Sets the support HttpMethods
/// </summary>
public ISet<string> HttpMethods { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Gets/Sets the path template for this path.
/// If it is set, it will be used to generate the path item.
/// </summary>
public string PathTemplate { get; set; }
/// <summary>
/// Gets the segments.
/// </summary>
@ -176,6 +187,17 @@ namespace Microsoft.OpenApi.OData.Edm
return sb.ToString();
}
internal bool SupportHttpMethod(string method)
{
// If the Httpmethods is empty, let it go
if (HttpMethods.Count == 0)
{
return true;
}
return HttpMethods.Contains(method);
}
/// <summary>
/// Push a segment to the last.
/// </summary>
@ -243,6 +265,11 @@ namespace Microsoft.OpenApi.OData.Edm
/// <returns>The string.</returns>
public override string ToString()
{
if (PathTemplate != null)
{
return PathTemplate;
}
return "/" + String.Join("/", Segments.Select(e => e.Kind));
}
@ -258,6 +285,16 @@ namespace Microsoft.OpenApi.OData.Edm
private ODataPathKind CalcPathType()
{
if (Segments.Count == 1 && Segments.First().Kind == ODataSegmentKind.Metadata)
{
return ODataPathKind.Metadata;
}
if (Segments.Last().Kind == ODataSegmentKind.DollarCount)
{
return ODataPathKind.DollarCount;
}
if (Segments.Any(c => c.Kind == ODataSegmentKind.StreamProperty || c.Kind == ODataSegmentKind.StreamContent))
{
return ODataPathKind.MediaEntity;

View file

@ -50,6 +50,16 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
MediaEntity,
/// <summary>
/// Represents a $metadata path
/// </summary>
Metadata,
/// <summary>
/// Represents a $count path, for example: ~/customers/$count
/// </summary>
DollarCount,
/// <summary>
/// Represents an un-supported/unknown path.
/// </summary>

View file

@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
namespace Microsoft.OpenApi.OData.Edm
{
@ -14,6 +15,11 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
public enum ODataSegmentKind
{
/// <summary>
/// $metadata
/// </summary>
Metadata,
/// <summary>
/// Navigation source (entity set or singleton )
/// </summary>
@ -57,7 +63,12 @@ namespace Microsoft.OpenApi.OData.Edm
/// <summary>
/// Stream property
/// </summary>
StreamProperty
StreamProperty,
/// <summary>
/// $count
/// </summary>
DollarCount,
}
/// <summary>

View file

@ -42,7 +42,14 @@ namespace Microsoft.OpenApi.OData.Generator
continue;
}
pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path));
if (path.PathTemplate != null)
{
pathItems.Add(path.PathTemplate, handler.CreatePathItem(context, path));
}
else
{
pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path));
}
}
if (settings.ShowRootPath)

View file

@ -0,0 +1,88 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Generator;
namespace Microsoft.OpenApi.OData.Operation
{
/// <summary>
/// Retrieve a $count get
/// </summary>
internal class DollarCountGetOperationHandler : OperationHandler
{
/// <inheritdoc/>
public override OperationType OperationType => OperationType.Get;
/// <summary>
/// Gets/sets the segment before $count.
/// this segment could be "entity set", "Collection property", "Composable function whose return is collection",etc.
/// </summary>
internal ODataSegment LastSecondSegment { get; set; }
/// <inheritdoc/>
protected override void Initialize(ODataContext context, ODataPath path)
{
base.Initialize(context, path);
// get the last second segment
int count = path.Segments.Count;
LastSecondSegment = path.Segments.ElementAt(count - 1);
}
/// <inheritdoc/>
protected override void SetBasicInfo(OpenApiOperation operation)
{
// Summary
operation.Summary = $"Get the number of the resource";
// OperationId
if (Context.Settings.EnableOperationId)
{
operation.OperationId = $"Get.Count.{LastSecondSegment.Identifier}";
}
base.SetBasicInfo(operation);
}
/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
{
OpenApiSchema schema = new OpenApiSchema
{
Type = "integer",
Format = "int32"
};
operation.Responses = new OpenApiResponses
{
{
Constants.StatusCode200,
new OpenApiResponse
{
Description = "The count of the resource",
Content = new Dictionary<string, OpenApiMediaType>
{
{
"text/plain",
new OpenApiMediaType
{
Schema = schema
}
}
}
}
}
};
operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());
base.SetResponses(operation);
}
}
}

View file

@ -63,6 +63,7 @@ namespace Microsoft.OpenApi.OData.Operation
base.SetResponses(operation);
}
/// <inheritdoc/>
protected override void SetSecurity(OpenApiOperation operation)
{

View file

@ -0,0 +1,77 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Generator;
namespace Microsoft.OpenApi.OData.Operation
{
/// <summary>
/// Retrieve a metadata document "get"
/// </summary>
internal class MetadataGetOperationHandler : OperationHandler
{
/// <inheritdoc/>
public override OperationType OperationType => OperationType.Get;
/// <inheritdoc/>
protected override void SetBasicInfo(OpenApiOperation operation)
{
// Summary
operation.Summary = $"Get OData metadata (CSDL) document";
// OperationId
if (Context.Settings.EnableOperationId)
{
string routePrefix = Context.Settings.PathPrefix ?? "";
if (Context.Settings.PathPrefix != null)
{
operation.OperationId = $"{routePrefix}.Get.Metadata";
}
else
{
operation.OperationId = "Get.Metadata";
}
}
base.SetBasicInfo(operation);
}
/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
{
OpenApiSchema schema = new OpenApiSchema
{
Type = "string"
};
operation.Responses = new OpenApiResponses
{
{
Constants.StatusCode200,
new OpenApiResponse
{
Description = "Retrieved metadata document",
Content = new Dictionary<string, OpenApiMediaType>
{
{
Constants.ApplicationXmlMediaType,
new OpenApiMediaType
{
Schema = schema
}
}
}
}
}
};
operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());
base.SetResponses(operation);
}
}
}

View file

@ -83,6 +83,18 @@ namespace Microsoft.OpenApi.OData.Operation
{OperationType.Get, new MediaEntityGetOperationHandler() },
{OperationType.Put, new MediaEntityPutOperationHandler() }
};
// $metadata operation (Get)
_handlers[ODataPathKind.Metadata] = new Dictionary<OperationType, IOperationHandler>
{
{OperationType.Get, new MetadataGetOperationHandler() }
};
// $count operation (Get)
_handlers[ODataPathKind.DollarCount] = new Dictionary<OperationType, IOperationHandler>
{
{OperationType.Get, new DollarCountGetOperationHandler() }
};
}
/// <inheritdoc/>

View file

@ -0,0 +1,25 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
namespace Microsoft.OpenApi.OData.PathItem
{
/// <summary>
/// Path item handler for $count.
/// </summary>
internal class DollarCountPathItemHandler : PathItemHandler
{
/// <inheritdoc/>
protected override ODataPathKind HandleKind => ODataPathKind.DollarCount;
/// <inheritdoc/>
protected override void SetOperations(OpenApiPathItem item)
{
AddOperation(item, OperationType.Get);
}
}
}

View file

@ -0,0 +1,25 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
namespace Microsoft.OpenApi.OData.PathItem
{
/// <summary>
/// Path item handler for $metadata.
/// </summary>
internal class MetadataPathItemHandler : PathItemHandler
{
/// <inheritdoc/>
protected override ODataPathKind HandleKind => ODataPathKind.Metadata;
/// <inheritdoc/>
protected override void SetOperations(OpenApiPathItem item)
{
AddOperation(item, OperationType.Get);
}
}
}

View file

@ -83,6 +83,12 @@ namespace Microsoft.OpenApi.OData.PathItem
/// <param name="operationType">The operation type.</param>
protected virtual void AddOperation(OpenApiPathItem item, OperationType operationType)
{
string httpMethod = operationType.ToString();
if (!Path.SupportHttpMethod(httpMethod))
{
return;
}
IOperationHandlerProvider provider = Context.OperationHanderProvider;
IOperationHandler operationHander = provider.GetHandler(Path.Kind, operationType);
item.AddOperation(operationType, operationHander.CreateOperation(Context, Path));

View file

@ -37,7 +37,13 @@ namespace Microsoft.OpenApi.OData.PathItem
{ ODataPathKind.Ref, new RefPathItemHandler() },
// Media Entity
{ODataPathKind.MediaEntity, new MediaEntityPathItemHandler() },
{ ODataPathKind.MediaEntity, new MediaEntityPathItemHandler() },
// $Metadata
{ ODataPathKind.Metadata, new MetadataPathItemHandler() },
// $count
{ ODataPathKind.DollarCount, new DollarCountPathItemHandler() },
// Unknown
{ ODataPathKind.Unknown, null },

View file

@ -34,6 +34,8 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
[InlineData(ODataPathKind.Ref, OperationType.Put, typeof(RefPutOperationHandler))]
[InlineData(ODataPathKind.MediaEntity, OperationType.Get, typeof(MediaEntityGetOperationHandler))]
[InlineData(ODataPathKind.MediaEntity, OperationType.Put, typeof(MediaEntityPutOperationHandler))]
[InlineData(ODataPathKind.Metadata, OperationType.Get, typeof(MetadataGetOperationHandler))]
[InlineData(ODataPathKind.DollarCount, OperationType.Get, typeof(DollarCountGetOperationHandler))]
public void GetHandlerReturnsCorrectOperationHandlerType(ODataPathKind pathKind, OperationType operationType, Type handlerType)
{
// Arrange

View file

@ -20,6 +20,8 @@ namespace Microsoft.OpenApi.OData.PathItem.Tests
[InlineData(ODataPathKind.OperationImport, typeof(OperationImportPathItemHandler))]
[InlineData(ODataPathKind.Ref, typeof(RefPathItemHandler))]
[InlineData(ODataPathKind.MediaEntity, typeof(MediaEntityPathItemHandler))]
[InlineData(ODataPathKind.Metadata, typeof(MetadataPathItemHandler))]
[InlineData(ODataPathKind.DollarCount, typeof(DollarCountPathItemHandler))]
public void GetHandlerReturnsCorrectHandlerType(ODataPathKind pathKind, Type handlerType)
{
// Arrange