Skip to content

Commit ac9c93e

Browse files
authored
Creates links for actions and functions (#205)
* Initial setup for retrieving links for actions/function * Don't get links to structured properties for coll. valued nav. props * Code refactoring * Remove whitespace * Re-order method params * Add tests for OpenApiLinkGenerator * Update tests
1 parent 4e00050 commit ac9c93e

File tree

9 files changed

+335
-60
lines changed

9 files changed

+335
-60
lines changed

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiLinkGenerator.cs

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,82 +19,112 @@ namespace Microsoft.OpenApi.OData.Generator
1919
internal static class OpenApiLinkGenerator
2020
{
2121
/// <summary>
22-
/// Create the collection of <see cref="OpenApiLink"/> object.
22+
/// Create the collection of <see cref="OpenApiLink"/> object for an entity or collection of entities.
2323
/// </summary>
2424
/// <param name="context">The OData context.</param>
2525
/// <param name="entityType">The Entity type.</param>
2626
/// <param name ="entityName">The name of the source of the <see cref="IEdmEntityType"/> object.</param>
2727
/// <param name="entityKind">"The kind of the source of the <see cref="IEdmEntityType"/> object.</param>
28-
/// <param name="parameters">"The list of parameters of the incoming operation.</param>
29-
/// <param name="navPropOperationId">Optional parameter: The operation id of the source of the NavigationProperty object.</param>
28+
/// <param name="path">The OData path of the operation the links are to be generated for.</param>
29+
/// <param name="parameters">"Optional: The list of parameters of the incoming operation.</param>
30+
/// <param name="navPropOperationId">Optional: The operation id of the source of the NavigationProperty object.</param>
3031
/// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns>
3132
public static IDictionary<string, OpenApiLink> CreateLinks(this ODataContext context,
32-
IEdmEntityType entityType, string entityName, string entityKind,
33-
IList<OpenApiParameter> parameters, string navPropOperationId = null)
33+
IEdmEntityType entityType, string entityName, string entityKind, ODataPath path,
34+
IList<OpenApiParameter> parameters = null, string navPropOperationId = null)
3435
{
35-
IDictionary<string, OpenApiLink> links = new Dictionary<string, OpenApiLink>();
36-
3736
Utils.CheckArgumentNull(context, nameof(context));
3837
Utils.CheckArgumentNull(entityType, nameof(entityType));
3938
Utils.CheckArgumentNullOrEmpty(entityName, nameof(entityName));
4039
Utils.CheckArgumentNullOrEmpty(entityKind, nameof(entityKind));
41-
Utils.CheckArgumentNull(parameters, nameof(parameters));
40+
Utils.CheckArgumentNull(path, nameof(path));
4241

43-
List<string> pathKeyNames = new List<string>();
42+
List<string> pathKeyNames = new();
4443

4544
// Fetch defined Id(s) from incoming parameters (if any)
46-
foreach (var parameter in parameters)
45+
if (parameters != null)
4746
{
48-
if (!string.IsNullOrEmpty(parameter.Description) &&
49-
parameter.Description.ToLower().Contains("key"))
47+
foreach (var parameter in parameters)
5048
{
51-
pathKeyNames.Add(parameter.Name);
49+
if (!string.IsNullOrEmpty(parameter.Description) &&
50+
parameter.Description.ToLower().Contains("key"))
51+
{
52+
pathKeyNames.Add(parameter.Name);
53+
}
5254
}
5355
}
5456

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

61-
switch (entityKind)
60+
// Valid only for non collection-valued navigation properties
61+
if (!lastSegmentIsColNavProp)
62+
{
63+
foreach (IEdmNavigationProperty navProp in entityType.NavigationProperties())
6264
{
63-
case "Navigation": // just for contained navigations
64-
operationPrefix = navPropOperationId;
65-
break;
66-
default: // EntitySet, Entity, Singleton
67-
operationPrefix = entityName;
68-
break;
69-
}
65+
string navPropName = navProp.Name;
66+
string operationId;
67+
string operationPrefix;
7068

71-
if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
72-
{
73-
operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
74-
}
75-
else
76-
{
77-
operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
78-
}
69+
switch (entityKind)
70+
{
71+
case "Navigation": // just for contained navigations
72+
operationPrefix = navPropOperationId;
73+
break;
74+
default: // EntitySet, Entity, Singleton
75+
operationPrefix = entityName;
76+
break;
77+
}
7978

80-
OpenApiLink link = new OpenApiLink
81-
{
82-
OperationId = operationId,
83-
Parameters = new Dictionary<string, RuntimeExpressionAnyWrapper>()
84-
};
79+
if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
80+
{
81+
operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
82+
}
83+
else
84+
{
85+
operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
86+
}
8587

86-
if (pathKeyNames.Any())
87-
{
88-
foreach (var pathKeyName in pathKeyNames)
88+
OpenApiLink link = new OpenApiLink
8989
{
90-
link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
90+
OperationId = operationId,
91+
Parameters = new Dictionary<string, RuntimeExpressionAnyWrapper>()
92+
};
93+
94+
if (pathKeyNames.Any())
95+
{
96+
foreach (var pathKeyName in pathKeyNames)
9197
{
92-
Any = new OpenApiString("$request.path." + pathKeyName)
93-
};
98+
link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
99+
{
100+
Any = new OpenApiString("$request.path." + pathKeyName)
101+
};
102+
}
94103
}
104+
105+
links[navProp.Name] = link;
95106
}
107+
}
96108

97-
links[navProp.Name] = link;
109+
// Get the Operations and OperationImport paths bound to this (collection of) entity.
110+
IEnumerable<ODataPath> operationPaths = context.AllPaths.Where(p => (p.Kind.Equals(ODataPathKind.Operation) || p.Kind.Equals(ODataPathKind.OperationImport)) &&
111+
path.GetPathItemName().Equals(p.Clone().Pop().GetPathItemName()));
112+
113+
// Generate links to the Operations and OperationImport operations.
114+
if (operationPaths.Any())
115+
{
116+
foreach (var operationPath in operationPaths)
117+
{
118+
OpenApiLink link = new()
119+
{
120+
OperationId = string.Join(".", operationPath.Segments.Select(x =>
121+
{
122+
return x.Kind.Equals(ODataSegmentKind.Key) ? x.EntityType.Name : x.Identifier;
123+
}))
124+
};
125+
126+
links[operationPath.LastSegment.Identifier] = link;
127+
}
98128
}
99129

100130
return links;

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiResponseGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOp
140140
Utils.CheckArgumentNull(path, nameof(path));
141141

142142
OpenApiResponses responses = new();
143-
143+
144144
if (operation.IsAction() && operation.ReturnType == null)
145145
{
146146
responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse());

src/Microsoft.OpenApi.OData.Reader/Operation/EntityGetOperationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected override void SetResponses(OpenApiOperation operation)
8989
if (Context.Settings.ShowLinks)
9090
{
9191
links = Context.CreateLinks(entityType: EntitySet.EntityType(), entityName: EntitySet.Name,
92-
entityKind: EntitySet.ContainerElementKind.ToString(), PathParameters);
92+
entityKind: EntitySet.ContainerElementKind.ToString(), path: Path, parameters: PathParameters);
9393
}
9494

9595
if (schema == null)

src/Microsoft.OpenApi.OData.Reader/Operation/NavigationPropertyGetOperationHandler.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ protected override void SetExtensions(OpenApiOperation operation)
7272
/// <inheritdoc/>
7373
protected override void SetResponses(OpenApiOperation operation)
7474
{
75+
IDictionary<string, OpenApiLink> links = null;
76+
if (Context.Settings.ShowLinks)
77+
{
78+
string operationId = GetOperationId();
79+
80+
links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
81+
entityKind: NavigationProperty.PropertyKind.ToString(), path: Path, parameters: PathParameters,
82+
navPropOperationId: operationId);
83+
}
84+
7585
if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
7686
{
7787
operation.Responses = new OpenApiResponses
@@ -85,7 +95,8 @@ protected override void SetResponses(OpenApiOperation operation)
8595
{
8696
Type = ReferenceType.Response,
8797
Id = $"{NavigationProperty.ToEntityType().FullName()}{Constants.CollectionSchemaSuffix}"
88-
}
98+
},
99+
Links = links
89100
}
90101
}
91102
};
@@ -112,15 +123,6 @@ protected override void SetResponses(OpenApiOperation operation)
112123
}
113124
};
114125
}
115-
IDictionary<string, OpenApiLink> links = null;
116-
if (Context.Settings.ShowLinks)
117-
{
118-
string operationId = GetOperationId();
119-
120-
links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
121-
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters,
122-
navPropOperationId: operationId);
123-
}
124126

125127
operation.Responses = new OpenApiResponses
126128
{

src/Microsoft.OpenApi.OData.Reader/Operation/RefGetOperationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ protected override void SetResponses(OpenApiOperation operation)
9999
string operationId = GetOperationId();
100100

101101
links = Context.CreateLinks(entityType: NavigationProperty.ToEntityType(), entityName: NavigationProperty.Name,
102-
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters,
102+
entityKind: NavigationProperty.PropertyKind.ToString(), parameters: PathParameters, path: Path,
103103
navPropOperationId: operationId);
104104
}
105105

src/Microsoft.OpenApi.OData.Reader/Operation/SingletonGetOperationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ protected override void SetResponses(OpenApiOperation operation)
8787
if (Context.Settings.ShowLinks)
8888
{
8989
links = Context.CreateLinks(entityType: Singleton.EntityType(), entityName: Singleton.Name,
90-
entityKind: Singleton.ContainerElementKind.ToString(), parameters: PathParameters);
90+
entityKind: Singleton.ContainerElementKind.ToString(), path: Path, parameters: PathParameters);
9191
}
9292

9393
if (schema == null)

0 commit comments

Comments
 (0)