-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathMediaEntityOperationalHandler.cs
221 lines (191 loc) · 8.4 KB
/
MediaEntityOperationalHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Vocabularies;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Vocabulary.Core;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.OpenApi.OData.Operation
{
/// <summary>
/// Base class for operation of media entity.
/// </summary>
internal abstract class MediaEntityOperationalHandler : NavigationPropertyOperationHandler
{
/// <summary>
/// Gets/Sets the NavigationSource segment
/// </summary>
protected ODataNavigationSourceSegment NavigationSourceSegment { get; private set; }
/// <summary>
/// Gets/Sets flag indicating whether path is navigation property path
/// </summary>
protected bool IsNavigationPropertyPath { get; private set; }
protected bool LastSegmentIsStreamPropertySegment { get; private set; }
/// <inheritdoc/>
protected override void Initialize(ODataContext context, ODataPath path)
{
// The first segment will either be an EntitySet navigation source or a Singleton navigation source
NavigationSourceSegment = path.FirstSegment as ODataNavigationSourceSegment;
// Check whether path is a navigation property path
IsNavigationPropertyPath = Path.Segments.Contains(
Path.Segments.Where(segment => segment is ODataNavigationPropertySegment).FirstOrDefault());
LastSegmentIsStreamPropertySegment = Path.LastSegment.Kind == ODataSegmentKind.StreamProperty;
if (IsNavigationPropertyPath)
{
// Initialize navigation property paths from base
base.Initialize(context, path);
}
}
/// <inheritdoc/>
protected override void SetTags(OpenApiOperation operation)
{
if (IsNavigationPropertyPath)
{
base.SetTags(operation);
}
else
{
string tagIdentifier = NavigationSourceSegment.Identifier + "." + NavigationSourceSegment.EntityType.Name;
OpenApiTag tag = new()
{
Name = tagIdentifier
};
// Use an extension for TOC (Table of Content)
tag.Extensions.Add(Constants.xMsTocType, new OpenApiString("page"));
operation.Tags.Add(tag);
Context.AppendTag(tag);
}
}
/// <inheritdoc/>
protected override void SetExtensions(OpenApiOperation operation)
{
base.SetExtensions(operation);
}
/// <summary>
/// Retrieves the operation Id for a media entity stream path.
/// </summary>
/// <param name="prefix">The http method identifier name.</param>
/// <param name="identifier">The stream segment identifier name.</param>
/// <returns></returns>
protected string GetOperationId(string prefix, string identifier)
{
Utils.CheckArgumentNullOrEmpty(prefix, nameof(prefix));
Utils.CheckArgumentNullOrEmpty(identifier, nameof(identifier));
IList<string> items = new List<string>
{
NavigationSourceSegment.Identifier
};
ODataSegment lastSegment = Path.Segments.Last(c => c is ODataStreamContentSegment || c is ODataStreamPropertySegment);
foreach (ODataSegment segment in Path.Segments.Skip(1))
{
if (segment == lastSegment)
{
if (!IsNavigationPropertyPath)
{
string typeName = NavigationSourceSegment.EntityType.Name;
items.Add(typeName);
items.Add(prefix + Utils.UpperFirstChar(identifier));
}
else
{
// Remove the last navigation property segment for navigation property paths,
// as this will be included within the prefixed name of the operation id
items.Remove(NavigationProperty.Name);
items.Add(prefix + Utils.UpperFirstChar(NavigationProperty.Name) + Utils.UpperFirstChar(identifier));
}
break;
}
else
{
if (segment is ODataNavigationPropertySegment npSegment)
{
items.Add(npSegment.NavigationProperty.Name);
}
}
}
return string.Join(".", items);
}
/// <summary>
/// Gets a media entity content description.
/// </summary>
/// <returns>The entity content description.</returns>
protected IDictionary<string, OpenApiMediaType> GetContentDescription()
{
var content = new Dictionary<string, OpenApiMediaType>();
OpenApiSchema schema = new OpenApiSchema
{
Type = "string",
Format = "binary"
};
// Fetch the respective AcceptableMediaTypes
(_, var property) = GetStreamElements();
IEnumerable<string> mediaTypes = null;
if (property != null)
{
mediaTypes = Context.Model.GetCollection(property,
CoreConstants.AcceptableMediaTypes);
}
if (mediaTypes != null)
{
foreach (string item in mediaTypes)
{
content.Add(item, null);
}
}
else
{
// Default content type
content.Add(Constants.ApplicationOctetStreamMediaType, new OpenApiMediaType
{
Schema = schema
});
};
return content;
}
/// <summary>
/// Gets the stream property and entity type declaring the stream property.
/// </summary>
/// <returns>The stream property and entity type declaring the stream property.</returns>
protected (IEdmEntityType entityType, IEdmProperty property) GetStreamElements()
{
// Only ODataStreamPropertySegment is annotatable
if (!LastSegmentIsStreamPropertySegment) return (null, null);
// Retrieve the entity type of the segment before the stream property segment
var entityType = Path.Segments.ElementAtOrDefault(Path.Segments.Count - 2).EntityType;
// The stream property can either be a structural type or a navigation property type
ODataSegment lastSegmentProp = Path.Segments.LastOrDefault(c => c is ODataStreamPropertySegment);
IEdmProperty property = GetStructuralProperty(entityType, lastSegmentProp.Identifier);
if (property == null)
{
property = GetNavigationProperty(entityType, lastSegmentProp.Identifier);
}
return (entityType, property);
}
private IEdmStructuralProperty GetStructuralProperty(IEdmEntityType entityType, string identifier)
{
return entityType.StructuralProperties().FirstOrDefault(x => x.Name.Equals(identifier));
}
private IEdmNavigationProperty GetNavigationProperty(IEdmEntityType entityType, string identifier)
{
return entityType.DeclaredNavigationProperties().FirstOrDefault(x => x.Name.Equals(identifier));
}
protected override void SetExternalDocs(OpenApiOperation operation)
{
if (Context.Settings.ShowExternalDocs && IsNavigationPropertyPath &&
Context.Model.GetLinkRecord(NavigationProperty, CustomLinkRel) is Link externalDocs)
{
operation.ExternalDocs = new OpenApiExternalDocs()
{
Description = CoreConstants.ExternalDocsDescription,
Url = externalDocs.Href
};
}
}
}
}