Skip to content

Commit 7874361

Browse files
Add delete operations for media entities (#523)
* Add delete operation to media entity * Update release notes * Update tests * Add more tests * Update pakage version
1 parent f8cef27 commit 7874361

15 files changed

+3506
-124
lines changed

src/Microsoft.OpenApi.OData.Reader/Microsoft.OpenAPI.OData.Reader.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
<TargetFrameworks>netstandard2.0</TargetFrameworks>
1616
<PackageId>Microsoft.OpenApi.OData</PackageId>
1717
<SignAssembly>true</SignAssembly>
18-
<Version>1.6.2</Version>
18+
<Version>1.6.3</Version>
1919
<Description>This package contains the codes you need to convert OData CSDL to Open API Document of Model.</Description>
2020
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
2121
<PackageTags>Microsoft OpenApi OData EDM</PackageTags>
2222
<RepositoryUrl>https://github.com/Microsoft/OpenAPI.NET.OData</RepositoryUrl>
2323
<PackageReleaseNotes>
24-
- Adds support for fetching annotations using Edm target path preferentially #514
24+
- Add delete operations to media entities #522
2525
</PackageReleaseNotes>
2626
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
2727
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using Microsoft.OData.Edm;
2+
using Microsoft.OpenApi.Models;
3+
using Microsoft.OpenApi.OData.Common;
4+
using Microsoft.OpenApi.OData.Edm;
5+
using Microsoft.OpenApi.OData.Generator;
6+
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
7+
using System.Linq;
8+
9+
namespace Microsoft.OpenApi.OData.Operation
10+
{
11+
internal class MediaEntityDeleteOperationHandler : MediaEntityOperationalHandler
12+
{
13+
/// <inheritdoc/>
14+
public override OperationType OperationType => OperationType.Delete;
15+
16+
private DeleteRestrictionsType _deleteRestrictions;
17+
18+
protected override void Initialize(ODataContext context, ODataPath path)
19+
{
20+
base.Initialize(context, path);
21+
22+
if (Property != null)
23+
{
24+
_deleteRestrictions = Context.Model.GetRecord<DeleteRestrictionsType>(TargetPath, CapabilitiesConstants.DeleteRestrictions);
25+
if (Property is IEdmNavigationProperty)
26+
{
27+
var navigationDeleteRestrictions = Context.Model.GetRecord<NavigationRestrictionsType>(Property, CapabilitiesConstants.NavigationRestrictions)?
28+
.RestrictedProperties?.FirstOrDefault()?.DeleteRestrictions;
29+
if (_deleteRestrictions != null && navigationDeleteRestrictions != null)
30+
{
31+
_deleteRestrictions.MergePropertiesIfNull(navigationDeleteRestrictions);
32+
}
33+
_deleteRestrictions ??= navigationDeleteRestrictions;
34+
}
35+
else
36+
{
37+
var propertyDeleteRestrictions = Context.Model.GetRecord<DeleteRestrictionsType>(Property, CapabilitiesConstants.DeleteRestrictions);
38+
if (_deleteRestrictions != null && propertyDeleteRestrictions != null)
39+
{
40+
_deleteRestrictions.MergePropertiesIfNull(propertyDeleteRestrictions);
41+
}
42+
_deleteRestrictions ??= propertyDeleteRestrictions;
43+
}
44+
}
45+
}
46+
47+
/// <inheritdoc/>
48+
protected override void SetBasicInfo(OpenApiOperation operation)
49+
{
50+
// Summary
51+
string placeholderValue = LastSegmentIsStreamPropertySegment ? Path.LastSegment.Identifier : "media content";
52+
operation.Summary = _deleteRestrictions?.Description;
53+
operation.Summary ??= IsNavigationPropertyPath
54+
? $"Delete {placeholderValue} for the navigation property {NavigationProperty.Name} in {NavigationSourceSegment.NavigationSource.Name}"
55+
: $"Delete {placeholderValue} for {NavigationSourceSegment.EntityType.Name} in {NavigationSourceSegment.Identifier}";
56+
57+
// Description
58+
operation.Description = _deleteRestrictions?.LongDescription ?? Context.Model.GetDescriptionAnnotation(Property);
59+
60+
// OperationId
61+
if (Context.Settings.EnableOperationId)
62+
{
63+
string identifier = LastSegmentIsStreamPropertySegment ? Path.LastSegment.Identifier : "Content";
64+
operation.OperationId = GetOperationId("Delete", identifier);
65+
}
66+
67+
base.SetBasicInfo(operation);
68+
}
69+
70+
/// <inheritdoc/>
71+
protected override void SetParameters(OpenApiOperation operation)
72+
{
73+
base.SetParameters(operation);
74+
75+
operation.Parameters.Add(new OpenApiParameter
76+
{
77+
Name = "If-Match",
78+
In = ParameterLocation.Header,
79+
Description = "ETag",
80+
Schema = new OpenApiSchema
81+
{
82+
Type = "string"
83+
}
84+
});
85+
}
86+
87+
/// <inheritdoc/>
88+
protected override void SetResponses(OpenApiOperation operation)
89+
{
90+
// Response for Delete methods should be 204 No Content
91+
OpenApiConvertSettings settings = Context.Settings.Clone();
92+
settings.UseSuccessStatusCodeRange = false;
93+
94+
operation.AddErrorResponses(settings, true);
95+
base.SetResponses(operation);
96+
}
97+
98+
protected override void SetSecurity(OpenApiOperation operation)
99+
{
100+
if (_deleteRestrictions == null || _deleteRestrictions.Permissions == null)
101+
{
102+
return;
103+
}
104+
105+
operation.Security = Context.CreateSecurityRequirements(_deleteRestrictions.Permissions).ToList();
106+
}
107+
108+
/// <inheritdoc/>
109+
protected override void AppendCustomParameters(OpenApiOperation operation)
110+
{
111+
if (_deleteRestrictions == null)
112+
{
113+
return;
114+
}
115+
116+
if (_deleteRestrictions.CustomHeaders != null)
117+
{
118+
AppendCustomParameters(operation, _deleteRestrictions.CustomHeaders, ParameterLocation.Header);
119+
}
120+
121+
if (_deleteRestrictions.CustomQueryOptions != null)
122+
{
123+
AppendCustomParameters(operation, _deleteRestrictions.CustomQueryOptions, ParameterLocation.Query);
124+
}
125+
}
126+
}
127+
}

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

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,22 @@ protected override void SetBasicInfo(OpenApiOperation operation)
6363
: $"Get {placeholderValue} for {NavigationSourceSegment.EntityType.Name} from {NavigationSourceSegment.Identifier}";
6464

6565
// Description
66-
if (LastSegmentIsStreamPropertySegment)
67-
{
68-
string description;
69-
70-
if (Property is IEdmNavigationProperty)
71-
{
72-
description = LastSegmentIsKeySegment
73-
? _readRestrictions?.ReadByKeyRestrictions?.LongDescription
74-
: _readRestrictions?.LongDescription
75-
?? Context.Model.GetDescriptionAnnotation(Property);
76-
}
77-
else
78-
{
79-
// Structural property
80-
description = _readRestrictions?.LongDescription ?? Context.Model.GetDescriptionAnnotation(Property);
81-
}
66+
string description;
8267

83-
operation.Description = description;
68+
if (Property is IEdmNavigationProperty)
69+
{
70+
description = LastSegmentIsKeySegment
71+
? _readRestrictions?.ReadByKeyRestrictions?.LongDescription
72+
: _readRestrictions?.LongDescription
73+
?? Context.Model.GetDescriptionAnnotation(Property);
8474
}
75+
else
76+
{
77+
// Structural property
78+
description = _readRestrictions?.LongDescription ?? Context.Model.GetDescriptionAnnotation(Property);
79+
}
80+
81+
operation.Description = description;
8582

8683
// OperationId
8784
if (Context.Settings.EnableOperationId)

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ internal abstract class MediaEntityOperationalHandler : OperationHandler
3131

3232
protected bool LastSegmentIsStreamPropertySegment { get; private set; }
3333

34+
protected bool LastSegmentIsStreamContentSegment { get; private set; }
35+
3436
/// <summary>
3537
/// Gets a bool value indicating whether the last segment is a key segment.
3638
/// </summary>
@@ -58,6 +60,8 @@ protected override void Initialize(ODataContext context, ODataPath path)
5860

5961
LastSegmentIsStreamPropertySegment = Path.LastSegment.Kind == ODataSegmentKind.StreamProperty;
6062

63+
LastSegmentIsStreamContentSegment = Path.LastSegment.Kind == ODataSegmentKind.StreamContent;
64+
6165
LastSegmentIsKeySegment = path.LastSegment is ODataKeySegment;
6266

6367
(_, Property) = GetStreamElements();
@@ -196,13 +200,17 @@ protected IDictionary<string, OpenApiMediaType> GetContentDescription()
196200
protected (IEdmEntityType entityType, IEdmProperty property) GetStreamElements()
197201
{
198202
// Only ODataStreamPropertySegment is annotatable
199-
if (!LastSegmentIsStreamPropertySegment) return (null, null);
203+
if (!LastSegmentIsStreamPropertySegment && !LastSegmentIsStreamContentSegment) return (null, null);
200204

201205
// Retrieve the entity type of the segment before the stream property segment
202-
var entityType = Path.Segments.ElementAtOrDefault(Path.Segments.Count - 2).EntityType;
206+
var entityType = LastSegmentIsStreamContentSegment
207+
? Path.Segments.ElementAtOrDefault(Path.Segments.Count - 3).EntityType
208+
: Path.Segments.ElementAtOrDefault(Path.Segments.Count - 2).EntityType;
203209

204210
// The stream property can either be a structural type or a navigation property type
205-
ODataSegment lastSegmentProp = Path.Segments.LastOrDefault(c => c is ODataStreamPropertySegment);
211+
ODataSegment lastSegmentProp = LastSegmentIsStreamContentSegment
212+
? Path.Segments.Reverse().Skip(1).FirstOrDefault()
213+
: Path.Segments.LastOrDefault(c => c is ODataStreamPropertySegment);
206214
IEdmProperty property = GetStructuralProperty(entityType, lastSegmentProp.Identifier);
207215
if (property == null)
208216
{

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,7 @@ protected override void SetBasicInfo(OpenApiOperation operation)
6565
: $"Update {placeholderValue} for {NavigationSourceSegment.EntityType.Name} in {NavigationSourceSegment.Identifier}";
6666

6767
// Description
68-
if (LastSegmentIsStreamPropertySegment)
69-
{
70-
operation.Description = _updateRestrictions?.LongDescription ?? Context.Model.GetDescriptionAnnotation(Property);
71-
}
68+
operation.Description = _updateRestrictions?.LongDescription ?? Context.Model.GetDescriptionAnnotation(Property);
7269

7370
// OperationId
7471
if (Context.Settings.EnableOperationId)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ protected override void SetResponses(OpenApiOperation operation)
8888
base.SetResponses(operation);
8989
}
9090

91+
/// <inheritdoc/>
9192
protected override void AppendCustomParameters(OpenApiOperation operation)
9293
{
9394
if (_deleteRestriction == null)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ private readonly IDictionary<ODataPathKind, IDictionary<OperationType, IOperatio
7373
{OperationType.Delete, new RefDeleteOperationHandler() }
7474
}},
7575

76-
// media entity operation (Get|Put)
76+
// media entity operation (Get|Put|Delete)
7777
{ODataPathKind.MediaEntity, new Dictionary<OperationType, IOperationHandler>
7878
{
7979
{OperationType.Get, new MediaEntityGetOperationHandler() },
80-
{OperationType.Put, new MediaEntityPutOperationHandler() }
80+
{OperationType.Put, new MediaEntityPutOperationHandler() },
81+
{OperationType.Delete, new MediaEntityDeleteOperationHandler() }
8182
}},
8283

8384
// $metadata operation (Get)

src/Microsoft.OpenApi.OData.Reader/PathItem/MediaEntityPathItemHandler.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ protected override void SetOperations(OpenApiPathItem item)
5050
{
5151
AddOperation(item, OperationType.Put);
5252
}
53+
54+
DeleteRestrictionsType delete = EntitySet != null
55+
? Context.Model.GetRecord<DeleteRestrictionsType>(EntitySet)
56+
: Context.Model.GetRecord<DeleteRestrictionsType>(Singleton);
57+
58+
if (delete == null || delete.IsDeletable)
59+
{
60+
AddOperation(item, OperationType.Delete);
61+
}
5362
}
5463

5564
/// <inheritdoc/>

0 commit comments

Comments
 (0)