1
- // ------------------------------------------------------------
1
+ // ------------------------------------------------------------
2
2
// Copyright (c) Microsoft Corporation. All rights reserved.
3
3
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4
4
// ------------------------------------------------------------
8
8
using System . Linq ;
9
9
using Microsoft . OData . Edm ;
10
10
using Microsoft . OpenApi . Models ;
11
+ using Microsoft . OpenApi . OData . Edm ;
11
12
using Microsoft . OpenApi . OData . Vocabulary . Capabilities ;
12
13
13
14
namespace Microsoft . OpenApi . OData . Common
@@ -22,7 +23,7 @@ internal static OpenApiSchema GetDerivedTypesReferenceSchema(IEdmStructuredType
22
23
{
23
24
Utils . CheckArgumentNull ( structuredType , nameof ( structuredType ) ) ;
24
25
Utils . CheckArgumentNull ( edmModel , nameof ( edmModel ) ) ;
25
- if ( structuredType is not IEdmSchemaElement schemaElement ) throw new ArgumentException ( "The type is not a schema element." , nameof ( structuredType ) ) ;
26
+ if ( structuredType is not IEdmSchemaElement schemaElement ) throw new ArgumentException ( "The type is not a schema element." , nameof ( structuredType ) ) ;
26
27
27
28
IEnumerable < IEdmSchemaElement > derivedTypes = edmModel . FindAllDerivedTypes ( structuredType ) . OfType < IEdmSchemaElement > ( ) ;
28
29
@@ -32,12 +33,12 @@ internal static OpenApiSchema GetDerivedTypesReferenceSchema(IEdmStructuredType
32
33
}
33
34
34
35
OpenApiSchema schema = new ( )
35
- {
36
+ {
36
37
OneOf = new List < OpenApiSchema > ( )
37
38
} ;
38
39
39
40
OpenApiSchema baseTypeSchema = new ( )
40
- {
41
+ {
41
42
UnresolvedReference = true ,
42
43
Reference = new OpenApiReference
43
44
{
@@ -50,7 +51,7 @@ internal static OpenApiSchema GetDerivedTypesReferenceSchema(IEdmStructuredType
50
51
foreach ( IEdmSchemaElement derivedType in derivedTypes )
51
52
{
52
53
OpenApiSchema derivedTypeSchema = new ( )
53
- {
54
+ {
54
55
UnresolvedReference = true ,
55
56
Reference = new OpenApiReference
56
57
{
@@ -62,14 +63,14 @@ internal static OpenApiSchema GetDerivedTypesReferenceSchema(IEdmStructuredType
62
63
} ;
63
64
64
65
return schema ;
65
- }
66
+ }
66
67
67
68
/// <summary>
68
69
/// Verifies whether the provided navigation restrictions allow for navigability of a navigation property.
69
70
/// </summary>
70
71
/// <param name="restrictionType">The <see cref="NavigationRestrictionsType"/>.</param>
71
72
/// <param name="restrictionProperty">The <see cref="NavigationPropertyRestriction"/>.</param>
72
- /// <returns></returns>
73
+ /// <returns>true, if navigability is allowed, otherwise false. </returns>
73
74
internal static bool NavigationRestrictionsAllowsNavigability (
74
75
NavigationRestrictionsType restrictionType ,
75
76
NavigationPropertyRestriction restrictionProperty )
@@ -83,7 +84,262 @@ internal static bool NavigationRestrictionsAllowsNavigability(
83
84
// if the individual has no navigability setting, use the global navigability setting
84
85
// Default navigability for all navigation properties of the annotation target.
85
86
// Individual navigation properties can override this value via `RestrictedProperties/Navigability`.
86
- return restrictionProperty ? . Navigability != null || restrictionType == null || restrictionType . IsNavigable ;
87
+ return restrictionProperty ? . Navigability != null || restrictionType == null || restrictionType . IsNavigable ;
88
+ }
89
+
90
+ /// <summary>
91
+ /// Generates the operation id from a navigation property path.
92
+ /// </summary>
93
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
94
+ /// <param name="prefix">Optional: Identifier indicating whether it is a collection-valued non-indexed or single-valued navigation property.</param>
95
+ /// <returns>The operation id generated from the given navigation property path.</returns>
96
+ internal static string GenerateNavigationPropertyPathOperationId ( ODataPath path , string prefix = null )
97
+ {
98
+ IList < string > items = RetrieveNavigationPropertyPathsOperationIdSegments ( path ) ;
99
+
100
+ if ( ! items . Any ( ) )
101
+ return null ;
102
+
103
+ int lastItemIndex = items . Count - 1 ;
104
+
105
+ if ( ! string . IsNullOrEmpty ( prefix ) )
106
+ {
107
+ items [ lastItemIndex ] = prefix + Utils . UpperFirstChar ( items . Last ( ) ) ;
108
+ }
109
+ else
110
+ {
111
+ items [ lastItemIndex ] = Utils . UpperFirstChar ( items . Last ( ) ) ;
112
+ }
113
+
114
+ return string . Join ( "." , items ) ;
115
+ }
116
+
117
+ /// <summary>
118
+ /// Generates the operation id from a complex property path.
119
+ /// </summary>
120
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
121
+ /// <param name="prefix">Optional: Identifier indicating whether it is a collection-valued or single-valued complex property.</param>
122
+ /// <returns>The operation id generated from the given complex property path.</returns>
123
+ internal static string GenerateComplexPropertyPathOperationId ( ODataPath path , string prefix = null )
124
+ {
125
+ IList < string > items = RetrieveNavigationPropertyPathsOperationIdSegments ( path ) ;
126
+
127
+ if ( ! items . Any ( ) )
128
+ return null ;
129
+
130
+ ODataComplexPropertySegment lastSegment = path . Segments . Skip ( 1 ) . OfType < ODataComplexPropertySegment > ( ) ? . Last ( ) ;
131
+ Utils . CheckArgumentNull ( lastSegment , nameof ( lastSegment ) ) ;
132
+
133
+ if ( ! string . IsNullOrEmpty ( prefix ) )
134
+ {
135
+ items . Add ( prefix + Utils . UpperFirstChar ( lastSegment ? . Identifier ) ) ;
136
+ }
137
+ else
138
+ {
139
+ items . Add ( Utils . UpperFirstChar ( lastSegment ? . Identifier ) ) ;
140
+ }
141
+
142
+ return string . Join ( "." , items ) ;
143
+ }
144
+
145
+ /// <summary>
146
+ /// Retrieves the segments of an operation id generated from a navigation property path.
147
+ /// </summary>
148
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
149
+ /// <returns>The segments of an operation id generated from the given navigation property path.</returns>
150
+ internal static IList < string > RetrieveNavigationPropertyPathsOperationIdSegments ( ODataPath path )
151
+ {
152
+ Utils . CheckArgumentNull ( path , nameof ( path ) ) ;
153
+
154
+ IEdmNavigationSource navigationSource = ( path . FirstSegment as ODataNavigationSourceSegment ) ? . NavigationSource ;
155
+ Utils . CheckArgumentNull ( navigationSource , nameof ( navigationSource ) ) ;
156
+
157
+ IList < string > items = new List < string >
158
+ {
159
+ navigationSource . Name
160
+ } ;
161
+
162
+ IEnumerable < ODataNavigationPropertySegment > navPropSegments = path . Segments . Skip ( 1 ) . OfType < ODataNavigationPropertySegment > ( ) ;
163
+ Utils . CheckArgumentNull ( navPropSegments , nameof ( navPropSegments ) ) ;
164
+
165
+ foreach ( var segment in navPropSegments )
166
+ {
167
+ if ( segment == navPropSegments . Last ( ) )
168
+ {
169
+ items . Add ( segment . NavigationProperty . Name ) ;
170
+ break ;
171
+ }
172
+ else
173
+ {
174
+ items . Add ( segment . NavigationProperty . Name ) ;
175
+ }
176
+ }
177
+
178
+ return items ;
179
+ }
180
+
181
+ /// <summary>
182
+ /// Generates the tag name from a navigation property path.
183
+ /// </summary>
184
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
185
+ /// <param name="context">The <see cref="ODataContext"/>.</param>
186
+ /// <returns>The tag name generated from the given navigation property path.</returns>
187
+ internal static string GenerateNavigationPropertyPathTagName ( ODataPath path , ODataContext context )
188
+ {
189
+ Utils . CheckArgumentNull ( path , nameof ( path ) ) ;
190
+ Utils . CheckArgumentNull ( context , nameof ( context ) ) ;
191
+
192
+ IEdmNavigationSource navigationSource = ( path . FirstSegment as ODataNavigationSourceSegment ) ? . NavigationSource ;
193
+
194
+ IList < string > items = new List < string >
195
+ {
196
+ navigationSource . Name
197
+ } ;
198
+
199
+ IEdmNavigationProperty navigationProperty = path . OfType < ODataNavigationPropertySegment > ( ) ? . Last ( ) ? . NavigationProperty ;
200
+ Utils . CheckArgumentNull ( navigationProperty , nameof ( navigationProperty ) ) ;
201
+
202
+ foreach ( var segment in path . Segments . Skip ( 1 ) . OfType < ODataNavigationPropertySegment > ( ) )
203
+ {
204
+ if ( segment . NavigationProperty == navigationProperty )
205
+ {
206
+ items . Add ( navigationProperty . ToEntityType ( ) . Name ) ;
207
+ break ;
208
+ }
209
+ else
210
+ {
211
+ if ( items . Count >= context . Settings . TagDepth - 1 )
212
+ {
213
+ items . Add ( segment . NavigationProperty . ToEntityType ( ) . Name ) ;
214
+ break ;
215
+ }
216
+ else
217
+ {
218
+ items . Add ( segment . NavigationProperty . Name ) ;
219
+ }
220
+ }
221
+ }
222
+
223
+ return string . Join ( "." , items ) ;
224
+ }
225
+
226
+ /// <summary>
227
+ /// Generates the tag name from a complex property path.
228
+ /// </summary>
229
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
230
+ /// <param name="context">The <see cref="ODataContext"/>.</param>
231
+ /// <returns>The tag name generated from the given complex property path.</returns>
232
+ internal static string GenerateComplexPropertyPathTagName ( ODataPath path , ODataContext context )
233
+ {
234
+ Utils . CheckArgumentNull ( path , nameof ( path ) ) ;
235
+
236
+ // Get the segment before the last complex type segment
237
+ ODataComplexPropertySegment complexSegment = path . Segments . OfType < ODataComplexPropertySegment > ( ) ? . Last ( ) ;
238
+ Utils . CheckArgumentNull ( complexSegment , nameof ( complexSegment ) ) ;
239
+
240
+ int complexSegmentIndex = path . Segments . IndexOf ( complexSegment ) ;
241
+ ODataSegment preComplexSegment = path . Segments . ElementAt ( complexSegmentIndex - 1 ) ;
242
+ string tagName = null ;
243
+
244
+ if ( preComplexSegment is ODataNavigationSourceSegment sourceSegment )
245
+ {
246
+ tagName = $ "{ sourceSegment . NavigationSource . Name } ";
247
+ }
248
+ else if ( preComplexSegment is ODataNavigationPropertySegment )
249
+ {
250
+ tagName = GenerateNavigationPropertyPathTagName ( path , context ) ;
251
+ }
252
+ else if ( preComplexSegment is ODataKeySegment )
253
+ {
254
+ var thirdLastSegment = path . Segments . ElementAt ( complexSegmentIndex - 2 ) ;
255
+ if ( thirdLastSegment is ODataNavigationPropertySegment )
256
+ {
257
+ tagName = GenerateNavigationPropertyPathTagName ( path , context ) ;
258
+ }
259
+ else if ( thirdLastSegment is ODataNavigationSourceSegment sourceSegment1 )
260
+ {
261
+ tagName = $ "{ sourceSegment1 . NavigationSource . Name } ";
262
+ }
263
+ }
264
+
265
+ List < string > tagNameItems = tagName ? . Split ( '.' ) . ToList ( ) ;
266
+
267
+ if ( tagNameItems . Count < context . Settings . TagDepth )
268
+ {
269
+ tagNameItems . Add ( complexSegment . ComplexType . Name ) ;
270
+ }
271
+
272
+ return string . Join ( "." , tagNameItems ) ;
273
+ }
274
+
275
+ /// <summary>
276
+ /// Generates the operation id prefix from an OData type cast path.
277
+ /// </summary>
278
+ /// <param name="path">The target <see cref="ODataPath"/>.</param>
279
+ /// <returns>The operation id prefix generated from the OData type cast path.</returns>
280
+ internal static string GenerateODataTypeCastPathOperationIdPrefix ( ODataPath path )
281
+ {
282
+ // Get the segment before the last OData type cast segment
283
+ ODataTypeCastSegment typeCastSegment = path . Segments . OfType < ODataTypeCastSegment > ( ) ? . Last ( ) ;
284
+ Utils . CheckArgumentNull ( typeCastSegment , nameof ( typeCastSegment ) ) ;
285
+
286
+ int typeCastSegmentIndex = path . Segments . IndexOf ( typeCastSegment ) ;
287
+
288
+ // The segment 1 place before the last OData type cast segment
289
+ ODataSegment secondLastSegment = path . Segments . ElementAt ( typeCastSegmentIndex - 1 ) ;
290
+
291
+ bool isIndexedCollValuedNavProp = false ;
292
+ if ( secondLastSegment is ODataKeySegment )
293
+ {
294
+ // The segment 2 places before the last OData type cast segment
295
+ ODataSegment thirdLastSegment = path . Segments . ElementAt ( typeCastSegmentIndex - 2 ) ;
296
+ if ( thirdLastSegment is ODataNavigationPropertySegment )
297
+ {
298
+ isIndexedCollValuedNavProp = true ;
299
+ }
300
+ }
301
+
302
+ ODataNavigationSourceSegment navigationSourceSegment = path . FirstSegment as ODataNavigationSourceSegment ;
303
+ IEdmSingleton singleton = navigationSourceSegment ? . NavigationSource as IEdmSingleton ;
304
+ IEdmEntitySet entitySet = navigationSourceSegment ? . NavigationSource as IEdmEntitySet ;
305
+
306
+ string operationId = null ;
307
+ if ( secondLastSegment is ODataComplexPropertySegment complexSegment )
308
+ {
309
+ string listOrGet = complexSegment . Property . Type . IsCollection ( ) ? "List" : "Get" ;
310
+ operationId = GenerateComplexPropertyPathOperationId ( path , listOrGet ) ;
311
+ }
312
+ else if ( secondLastSegment as ODataNavigationPropertySegment is not null || isIndexedCollValuedNavProp )
313
+ {
314
+ string prefix = "Get" ;
315
+ if ( ! isIndexedCollValuedNavProp &&
316
+ ( secondLastSegment as ODataNavigationPropertySegment ) ? . NavigationProperty . TargetMultiplicity ( ) == EdmMultiplicity . Many )
317
+ {
318
+ prefix = "List" ;
319
+ }
320
+
321
+ operationId = GenerateNavigationPropertyPathOperationId ( path , prefix ) ;
322
+ }
323
+ else if ( secondLastSegment is ODataKeySegment keySegment && ! isIndexedCollValuedNavProp )
324
+ {
325
+ string entityTypeName = keySegment . EntityType . Name ;
326
+ string operationName = $ "Get{ Utils . UpperFirstChar ( entityTypeName ) } ";
327
+ if ( keySegment . IsAlternateKey )
328
+ {
329
+ string alternateKeyName = string . Join ( "" , keySegment . Identifier . Split ( ',' ) . Select ( static x => Utils . UpperFirstChar ( x ) ) ) ;
330
+ operationName = $ "{ operationName } By{ alternateKeyName } ";
331
+ }
332
+ operationId = ( entitySet != null ) ? entitySet . Name : singleton . Name ;
333
+ operationId += $ ".{ entityTypeName } .{ operationName } ";
334
+ }
335
+ else if ( secondLastSegment is ODataNavigationSourceSegment )
336
+ {
337
+ operationId = ( entitySet != null )
338
+ ? entitySet . Name + "." + entitySet . EntityType ( ) . Name + ".List" + Utils . UpperFirstChar ( entitySet . EntityType ( ) . Name )
339
+ : singleton . Name + "." + singleton . EntityType ( ) . Name + ".Get" + Utils . UpperFirstChar ( singleton . EntityType ( ) . Name ) ;
340
+ }
341
+
342
+ return operationId ;
87
343
}
88
344
}
89
345
}
0 commit comments