Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates links for actions and functions #205

Merged
merged 7 commits into from
Mar 30, 2022
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
122 changes: 76 additions & 46 deletions src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiLinkGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,112 @@ namespace Microsoft.OpenApi.OData.Generator
internal static class OpenApiLinkGenerator
{
/// <summary>
/// Create the collection of <see cref="OpenApiLink"/> object.
/// Create the collection of <see cref="OpenApiLink"/> object for an entity or collection of entities.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="entityType">The Entity type.</param>
/// <param name ="entityName">The name of the source of the <see cref="IEdmEntityType"/> object.</param>
/// <param name="entityKind">"The kind of the source of the <see cref="IEdmEntityType"/> object.</param>
/// <param name="parameters">"The list of parameters of the incoming operation.</param>
/// <param name="navPropOperationId">Optional parameter: The operation id of the source of the NavigationProperty object.</param>
/// <param name="path">The OData path of the operation the links are to be generated for.</param>
/// <param name="parameters">"Optional: The list of parameters of the incoming operation.</param>
/// <param name="navPropOperationId">Optional: The operation id of the source of the NavigationProperty object.</param>
/// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns>
public static IDictionary<string, OpenApiLink> CreateLinks(this ODataContext context,
IEdmEntityType entityType, string entityName, string entityKind,
IList<OpenApiParameter> parameters, string navPropOperationId = null)
IEdmEntityType entityType, string entityName, string entityKind, ODataPath path,
IList<OpenApiParameter> parameters = null, string navPropOperationId = null)
{
IDictionary<string, OpenApiLink> links = new Dictionary<string, OpenApiLink>();

Utils.CheckArgumentNull(context, nameof(context));
Utils.CheckArgumentNull(entityType, nameof(entityType));
Utils.CheckArgumentNullOrEmpty(entityName, nameof(entityName));
Utils.CheckArgumentNullOrEmpty(entityKind, nameof(entityKind));
Utils.CheckArgumentNull(parameters, nameof(parameters));
Utils.CheckArgumentNull(path, nameof(path));

List<string> pathKeyNames = new List<string>();
List<string> pathKeyNames = new();

// Fetch defined Id(s) from incoming parameters (if any)
foreach (var parameter in parameters)
if (parameters != null)
{
if (!string.IsNullOrEmpty(parameter.Description) &&
parameter.Description.ToLower().Contains("key"))
foreach (var parameter in parameters)
{
pathKeyNames.Add(parameter.Name);
if (!string.IsNullOrEmpty(parameter.Description) &&
parameter.Description.ToLower().Contains("key"))
{
pathKeyNames.Add(parameter.Name);
}
}
}

foreach (IEdmNavigationProperty navProp in entityType.NavigationProperties())
{
string navPropName = navProp.Name;
string operationId;
string operationPrefix;
Dictionary<string, OpenApiLink> links = new();
bool lastSegmentIsColNavProp = (path.LastSegment as ODataNavigationPropertySegment)?.NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many;

switch (entityKind)
// Valid only for non collection-valued navigation properties
if (!lastSegmentIsColNavProp)
{
foreach (IEdmNavigationProperty navProp in entityType.NavigationProperties())
{
case "Navigation": // just for contained navigations
operationPrefix = navPropOperationId;
break;
default: // EntitySet, Entity, Singleton
operationPrefix = entityName;
break;
}
string navPropName = navProp.Name;
string operationId;
string operationPrefix;

if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
{
operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
}
else
{
operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
}
switch (entityKind)
{
case "Navigation": // just for contained navigations
operationPrefix = navPropOperationId;
break;
default: // EntitySet, Entity, Singleton
operationPrefix = entityName;
break;
}

OpenApiLink link = new OpenApiLink
{
OperationId = operationId,
Parameters = new Dictionary<string, RuntimeExpressionAnyWrapper>()
};
if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
{
operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
}
else
{
operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
}

if (pathKeyNames.Any())
{
foreach (var pathKeyName in pathKeyNames)
OpenApiLink link = new OpenApiLink
{
link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
OperationId = operationId,
Parameters = new Dictionary<string, RuntimeExpressionAnyWrapper>()
};

if (pathKeyNames.Any())
{
foreach (var pathKeyName in pathKeyNames)
{
Any = new OpenApiString("$request.path." + pathKeyName)
};
link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
{
Any = new OpenApiString("$request.path." + pathKeyName)
};
}
}

links[navProp.Name] = link;
}
}

links[navProp.Name] = link;
// Get the Operations and OperationImport paths bound to this (collection of) entity.
IEnumerable<ODataPath> operationPaths = context.AllPaths.Where(p => (p.Kind.Equals(ODataPathKind.Operation) || p.Kind.Equals(ODataPathKind.OperationImport)) &&
path.GetPathItemName().Equals(p.Clone().Pop().GetPathItemName()));

// Generate links to the Operations and OperationImport operations.
if (operationPaths.Any())
{
foreach (var operationPath in operationPaths)
{
OpenApiLink link = new()
{
OperationId = string.Join(".", operationPath.Segments.Select(x =>
{
return x.Kind.Equals(ODataSegmentKind.Key) ? x.EntityType.Name : x.Identifier;
}))
};

links[operationPath.LastSegment.Identifier] = link;
}
}

return links;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOp
Utils.CheckArgumentNull(path, nameof(path));

OpenApiResponses responses = new();

if (operation.IsAction() && operation.ReturnType == null)
{
responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ protected override void SetResponses(OpenApiOperation operation)
if (Context.Settings.ShowLinks)
{
links = Context.CreateLinks(entityType: EntitySet.EntityType(), entityName: EntitySet.Name,
entityKind: EntitySet.ContainerElementKind.ToString(), PathParameters);
entityKind: EntitySet.ContainerElementKind.ToString(), path: Path, parameters: PathParameters);
}

if (schema == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ protected override void SetExtensions(OpenApiOperation operation)
/// <inheritdoc/>
protected override void SetResponses(OpenApiOperation operation)
{
IDictionary<string, OpenApiLink> links = null;
if (Context.Settings.ShowLinks)
{
string operationId = GetOperationId();

links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
entityKind: NavigationProperty.PropertyKind.ToString(), path: Path, parameters: PathParameters,
navPropOperationId: operationId);
}

if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
operation.Responses = new OpenApiResponses
Expand All @@ -85,7 +95,8 @@ protected override void SetResponses(OpenApiOperation operation)
{
Type = ReferenceType.Response,
Id = $"{NavigationProperty.ToEntityType().FullName()}{Constants.CollectionSchemaSuffix}"
}
},
Links = links
}
}
};
Expand All @@ -112,15 +123,6 @@ protected override void SetResponses(OpenApiOperation operation)
}
};
}
IDictionary<string, OpenApiLink> links = null;
if (Context.Settings.ShowLinks)
{
string operationId = GetOperationId();

links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters,
navPropOperationId: operationId);
}

operation.Responses = new OpenApiResponses
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ protected override void SetResponses(OpenApiOperation operation)
string operationId = GetOperationId();

links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters,
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters, path: Path,
navPropOperationId: operationId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected override void SetResponses(OpenApiOperation operation)
if (Context.Settings.ShowLinks)
{
links = Context.CreateLinks(entityType: Singleton.EntityType(), entityName: Singleton.Name,
entityKind: Singleton.ContainerElementKind.ToString(), parameters: PathParameters);
entityKind: Singleton.ContainerElementKind.ToString(), path: Path, parameters: PathParameters);
}

if (schema == null)
Expand Down
Loading