- draft implementation of odata cast segments
This commit is contained in:
parent
b5b9e08908
commit
9716b1d0bf
|
@ -289,13 +289,15 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
{
|
||||
return ODataPathKind.Metadata;
|
||||
}
|
||||
|
||||
if (Segments.Last().Kind == ODataSegmentKind.DollarCount)
|
||||
else if (Segments.Last().Kind == ODataSegmentKind.DollarCount)
|
||||
{
|
||||
return ODataPathKind.DollarCount;
|
||||
}
|
||||
|
||||
if (Segments.Any(c => c.Kind == ODataSegmentKind.StreamProperty || c.Kind == ODataSegmentKind.StreamContent))
|
||||
else if (Segments.Last().Kind == ODataSegmentKind.TypeCast)
|
||||
{
|
||||
return ODataPathKind.TypeCast;
|
||||
}
|
||||
else if (Segments.Any(c => c.Kind == ODataSegmentKind.StreamProperty || c.Kind == ODataSegmentKind.StreamContent))
|
||||
{
|
||||
return ODataPathKind.MediaEntity;
|
||||
}
|
||||
|
@ -315,22 +317,17 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
{
|
||||
return ODataPathKind.NavigationProperty;
|
||||
}
|
||||
|
||||
if (Segments.Count == 1)
|
||||
else if (Segments.Count == 1 && Segments[0] is ODataNavigationSourceSegment segment)
|
||||
{
|
||||
ODataNavigationSourceSegment segment = Segments[0] as ODataNavigationSourceSegment;
|
||||
if (segment != null)
|
||||
if (segment.NavigationSource is IEdmSingleton)
|
||||
{
|
||||
if (segment.NavigationSource is IEdmSingleton)
|
||||
{
|
||||
return ODataPathKind.Singleton;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ODataPathKind.EntitySet;
|
||||
}
|
||||
return ODataPathKind.Singleton;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ODataPathKind.EntitySet;
|
||||
}
|
||||
}
|
||||
else if (Segments.Count == 2 && Segments.Last().Kind == ODataSegmentKind.Key)
|
||||
{
|
||||
return ODataPathKind.Entity;
|
||||
|
|
|
@ -60,9 +60,14 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
/// </summary>
|
||||
DollarCount,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type cast path, for example: ~/groups/{id}/members/microsoft.graph.user
|
||||
/// </summary>
|
||||
TypeCast,
|
||||
|
||||
/// <summary>
|
||||
/// Represents an un-supported/unknown path.
|
||||
/// </summary>
|
||||
Unknown
|
||||
}
|
||||
Unknown,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
ODataPathKind kind = path.Kind;
|
||||
switch(kind)
|
||||
{
|
||||
case ODataPathKind.TypeCast:
|
||||
case ODataPathKind.DollarCount:
|
||||
case ODataPathKind.Entity:
|
||||
case ODataPathKind.EntitySet:
|
||||
|
@ -285,12 +286,25 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
IEdmEntityType navEntityType = navigationProperty.ToEntityType();
|
||||
var targetsMany = navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many;
|
||||
var propertyPath = navigationProperty.GetPartnerPath()?.Path;
|
||||
var propertyPathIsEmpty = string.IsNullOrEmpty(propertyPath);
|
||||
|
||||
if (targetsMany && (string.IsNullOrEmpty(propertyPath) ||
|
||||
(count?.IsNonCountableNavigationProperty(propertyPath) ?? true)))
|
||||
if (targetsMany)
|
||||
{
|
||||
// ~/entityset/{key}/collection-valued-Nav/$count
|
||||
CreateCountPath(currentPath, convertSettings);
|
||||
if(propertyPathIsEmpty ||
|
||||
(count?.IsNonCountableNavigationProperty(propertyPath) ?? true))
|
||||
{
|
||||
// ~/entityset/{key}/collection-valued-Nav/$count
|
||||
CreateCountPath(currentPath, convertSettings);
|
||||
}
|
||||
|
||||
//TODO read the cast restrictions annotation
|
||||
var derivedTypes = _model
|
||||
.FindAllDerivedTypes(navigationProperty.DeclaringType)
|
||||
.Where(x => x.TypeKind == EdmTypeKind.Entity)
|
||||
.OfType<IEdmEntityType>()
|
||||
.ToArray();
|
||||
if(derivedTypes.Any())
|
||||
CreateTypeCastPaths(currentPath, convertSettings, derivedTypes);
|
||||
}
|
||||
|
||||
if (!navigationProperty.ContainsTarget)
|
||||
|
@ -393,6 +407,26 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
AppendPath(countPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create OData type cast paths.
|
||||
/// </summary>
|
||||
/// <param name="currentPath">The current OData path.</param>
|
||||
/// <param name="convertSettings">The settings for the current conversion.</param>
|
||||
/// <param name="targetTypes">The target types to generate a path for.</param>
|
||||
private void CreateTypeCastPaths(ODataPath currentPath, OpenApiConvertSettings convertSettings, params IEdmEntityType[] targetTypes)
|
||||
{
|
||||
if(currentPath == null) throw new ArgumentNullException(nameof(currentPath));
|
||||
if(convertSettings == null) throw new ArgumentNullException(nameof(convertSettings));
|
||||
if(!convertSettings.EnableODataTypeCast || targetTypes == null || !targetTypes.Any()) return;
|
||||
|
||||
foreach(var targetType in targetTypes)
|
||||
{
|
||||
var castPath = currentPath.Clone();
|
||||
castPath.Push(new ODataTypeCastSegment(targetType));
|
||||
AppendPath(castPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all bounding <see cref="IEdmOperation"/>.
|
||||
/// </summary>
|
||||
|
|
|
@ -188,6 +188,11 @@ namespace Microsoft.OpenApi.OData
|
|||
/// </summary>
|
||||
public bool EnableDollarCountPath { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets a value indicating whether or not to include the OData type cast segments on entity sets.
|
||||
/// </summary>
|
||||
public bool EnableODataTypeCast { get; set; } = true;
|
||||
|
||||
internal OpenApiConvertSettings Clone()
|
||||
{
|
||||
var newSettings = new OpenApiConvertSettings
|
||||
|
@ -219,6 +224,7 @@ namespace Microsoft.OpenApi.OData
|
|||
ShowRootPath = this.ShowRootPath,
|
||||
PathProvider = this.PathProvider,
|
||||
EnableDollarCountPath = this.EnableDollarCountPath,
|
||||
EnableODataTypeCast = this.EnableODataTypeCast,
|
||||
};
|
||||
|
||||
return newSettings;
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.OData.Edm;
|
||||
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>
|
||||
/// Retrieves a .../namespace.typename get
|
||||
/// </summary>
|
||||
internal class ODataTypeCastGetOperationHandler : OperationHandler
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override OperationType OperationType => OperationType.Get;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the segment before cast.
|
||||
/// this segment could be "entity set", "Collection property", etc.
|
||||
/// </summary>
|
||||
internal ODataSegment LastSecondSegment { get; set; }
|
||||
|
||||
private IEdmEntityType parentEntityType;
|
||||
private IEdmEntityType targetEntityType;
|
||||
private const int SecondLastSegmentIndex = 2;
|
||||
/// <inheritdoc/>
|
||||
protected override void Initialize(ODataContext context, ODataPath path)
|
||||
{
|
||||
base.Initialize(context, path);
|
||||
|
||||
// get the last second segment
|
||||
int count = path.Segments.Count;
|
||||
if(count >= SecondLastSegmentIndex)
|
||||
LastSecondSegment = path.Segments.ElementAt(count - SecondLastSegmentIndex);
|
||||
|
||||
parentEntityType = LastSecondSegment.EntityType;
|
||||
if(path.Last() is ODataTypeCastSegment oDataTypeCastSegment)
|
||||
{
|
||||
targetEntityType = oDataTypeCastSegment.EntityType;
|
||||
}
|
||||
else throw new NotImplementedException($"type cast type {path.Last().GetType().FullName} not implemented");
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetBasicInfo(OpenApiOperation operation)
|
||||
{
|
||||
// Summary
|
||||
operation.Summary = $"Get the items of type {targetEntityType.ShortQualifiedName()} in the {parentEntityType.ShortQualifiedName()} collection";
|
||||
|
||||
// OperationId
|
||||
if (Context.Settings.EnableOperationId)
|
||||
{
|
||||
operation.OperationId = $"Get.{parentEntityType.ShortQualifiedName()}.As.{targetEntityType.ShortQualifiedName()}";
|
||||
}
|
||||
|
||||
base.SetBasicInfo(operation);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetResponses(OpenApiOperation operation)
|
||||
{
|
||||
|
||||
OpenApiSchema schema = null;
|
||||
|
||||
if (Context.Settings.EnableDerivedTypesReferencesForResponses)
|
||||
{
|
||||
schema = EdmModelHelper.GetDerivedTypesReferenceSchema(parentEntityType, Context.Model);
|
||||
}
|
||||
|
||||
if (schema == null)
|
||||
{
|
||||
schema = new OpenApiSchema
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.Schema,
|
||||
Id = $"{parentEntityType.FullName()}.To.{targetEntityType.FullName()}"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var properties = new Dictionary<string, OpenApiSchema>
|
||||
{
|
||||
{
|
||||
"value",
|
||||
new OpenApiSchema
|
||||
{
|
||||
Type = "array",
|
||||
Items = schema
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (Context.Settings.EnablePagination)
|
||||
{
|
||||
properties.Add(
|
||||
"@odata.nextLink",
|
||||
new OpenApiSchema
|
||||
{
|
||||
Type = "string"
|
||||
});
|
||||
}
|
||||
|
||||
operation.Responses = new OpenApiResponses
|
||||
{
|
||||
{
|
||||
Constants.StatusCode200,
|
||||
new OpenApiResponse
|
||||
{
|
||||
Description = "Retrieved entities",
|
||||
Content = new Dictionary<string, OpenApiMediaType>
|
||||
{
|
||||
{
|
||||
Constants.ApplicationJsonMediaType,
|
||||
new OpenApiMediaType
|
||||
{
|
||||
Schema = new OpenApiSchema
|
||||
{
|
||||
Title = $"Collection of items of type {targetEntityType.ShortQualifiedName()} in the {parentEntityType.ShortQualifiedName()} collection",
|
||||
Type = "object",
|
||||
Properties = properties
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
operation.Responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse());
|
||||
|
||||
base.SetResponses(operation);
|
||||
}
|
||||
//TODO query parameters?
|
||||
//TODO extensions?
|
||||
}
|
||||
//TODO unit tests
|
|
@ -89,6 +89,12 @@ namespace Microsoft.OpenApi.OData.Operation
|
|||
{
|
||||
{OperationType.Get, new DollarCountGetOperationHandler() }
|
||||
}},
|
||||
|
||||
// .../namespace.typename (cast, get)
|
||||
{ODataPathKind.TypeCast, new Dictionary<OperationType, IOperationHandler>
|
||||
{
|
||||
{OperationType.Get, new ODataTypeCastGetOperationHandler() },
|
||||
}},
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// ------------------------------------------------------------
|
||||
// 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 type cast for example: ~/groups/{id}/members/microsoft.graph.user
|
||||
/// </summary>
|
||||
internal class ODataTypeCastPathItemHandler : PathItemHandler
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override ODataPathKind HandleKind => ODataPathKind.TypeCast;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetOperations(OpenApiPathItem item)
|
||||
{
|
||||
AddOperation(item, OperationType.Get);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO unit test for the ODataTypeCastPathItemHandler
|
|
@ -45,6 +45,9 @@ namespace Microsoft.OpenApi.OData.PathItem
|
|||
// $count
|
||||
{ ODataPathKind.DollarCount, new DollarCountPathItemHandler() },
|
||||
|
||||
// ~/groups/{id}/members/microsoft.graph.user
|
||||
{ ODataPathKind.TypeCast, new ODataTypeCastPathItemHandler() },
|
||||
|
||||
// Unknown
|
||||
{ ODataPathKind.Unknown, null },
|
||||
};
|
||||
|
|
|
@ -61,7 +61,8 @@ Microsoft.OpenApi.OData.Edm.ODataPathKind.Operation = 3 -> Microsoft.OpenApi.ODa
|
|||
Microsoft.OpenApi.OData.Edm.ODataPathKind.OperationImport = 4 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathKind.Ref = 6 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathKind.Singleton = 2 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathKind.Unknown = 10 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathKind.TypeCast = 10 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathKind.Unknown = 11 -> Microsoft.OpenApi.OData.Edm.ODataPathKind
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathProvider
|
||||
Microsoft.OpenApi.OData.Edm.ODataPathProvider.ODataPathProvider() -> void
|
||||
Microsoft.OpenApi.OData.Edm.ODataRefSegment
|
||||
|
@ -132,6 +133,8 @@ Microsoft.OpenApi.OData.OpenApiConvertSettings.PathProvider.get -> Microsoft.Ope
|
|||
Microsoft.OpenApi.OData.OpenApiConvertSettings.PathProvider.set -> void
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableDollarCountPath.get -> bool
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableDollarCountPath.set -> void
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableODataTypeCast.get -> bool
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.EnableODataTypeCast.set -> void
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.PrefixEntityTypeNameBeforeKey.get -> bool
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.PrefixEntityTypeNameBeforeKey.set -> void
|
||||
Microsoft.OpenApi.OData.OpenApiConvertSettings.RequireDerivedTypesConstraintForBoundOperations.get -> bool
|
||||
|
|
Loading…
Reference in a new issue