Skip to content

Commit 6b30bb0

Browse files
committed
support put operations on entities through update restrictions
annotation
1 parent fb9e753 commit 6b30bb0

File tree

5 files changed

+173
-5
lines changed

5 files changed

+173
-5
lines changed

docs/csdl/TripService.xml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,6 @@
117117
<Parameter Name="lon" Type="Edm.Double" Nullable="false" />
118118
<ReturnType Type="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport" />
119119
</Function>
120-
<Function Name="GetFavoriteAirline" IsBound="true" EntitySetPath="person">
121-
<Parameter Name="person" Type="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person" />
122-
<ReturnType Type="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airline" />
123-
</Function>
124120
<Function Name="GetFriendsTrips" IsBound="true">
125121
<Parameter Name="person" Type="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person" />
126122
<Parameter Name="userName" Type="Edm.String" Nullable="false" Unicode="false" />
@@ -154,6 +150,13 @@
154150
<PropertyPath>Name</PropertyPath>
155151
</Collection>
156152
</Annotation>
153+
<Annotation Term="Org.OData.Capabilities.V1.UpdateRestrictions">
154+
<Record>
155+
<PropertyValue Property="UpdateMethod">
156+
<EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
157+
</PropertyValue>
158+
</Record>
159+
</Annotation>
157160
</EntitySet>
158161
<EntitySet Name="Airports" EntityType="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airport" />
159162
<EntitySet Name="NewComePeople" EntityType="Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person" />
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// ------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4+
// ------------------------------------------------------------
5+
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using Microsoft.OData.Edm;
9+
using Microsoft.OpenApi.Models;
10+
using Microsoft.OpenApi.OData.Common;
11+
using Microsoft.OpenApi.OData.Edm;
12+
using Microsoft.OpenApi.OData.Generator;
13+
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
14+
15+
namespace Microsoft.OpenApi.OData.Operation
16+
{
17+
/// <summary>
18+
/// Update an Entity
19+
/// The Path Item Object for the entity set contains the keyword put with an Operation Object as value
20+
/// that describes the capabilities for updating the entity.
21+
/// </summary>
22+
internal class EntityPutOperationHandler : EntitySetOperationHandler
23+
{
24+
/// <inheritdoc/>
25+
public override OperationType OperationType => OperationType.Put;
26+
27+
/// <inheritdoc/>
28+
protected override void SetBasicInfo(OpenApiOperation operation)
29+
{
30+
// Summary
31+
operation.Summary = "Update entity in " + EntitySet.Name;
32+
33+
IEdmEntityType entityType = EntitySet.EntityType();
34+
35+
// Description
36+
operation.Description = Context.Model.GetDescriptionAnnotation(entityType);
37+
38+
// OperationId
39+
if (Context.Settings.EnableOperationId)
40+
{
41+
string typeName = entityType.Name;
42+
operation.OperationId = EntitySet.Name + "." + typeName + ".Update" + Utils.UpperFirstChar(typeName);
43+
}
44+
}
45+
46+
/// <inheritdoc/>
47+
protected override void SetRequestBody(OpenApiOperation operation)
48+
{
49+
OpenApiSchema schema = null;
50+
51+
if (Context.Settings.EnableDerivedTypesReferencesForRequestBody)
52+
{
53+
schema = EdmModelHelper.GetDerivedTypesReferenceSchema(EntitySet.EntityType(), Context.Model);
54+
}
55+
56+
if (schema == null)
57+
{
58+
schema = new OpenApiSchema
59+
{
60+
Reference = new OpenApiReference
61+
{
62+
Type = ReferenceType.Schema,
63+
Id = EntitySet.EntityType().FullName()
64+
}
65+
};
66+
}
67+
68+
operation.RequestBody = new OpenApiRequestBody
69+
{
70+
Required = true,
71+
Description = "New property values",
72+
Content = new Dictionary<string, OpenApiMediaType>
73+
{
74+
{
75+
Constants.ApplicationJsonMediaType, new OpenApiMediaType
76+
{
77+
Schema = schema
78+
}
79+
}
80+
}
81+
};
82+
83+
base.SetRequestBody(operation);
84+
}
85+
86+
/// <inheritdoc/>
87+
protected override void SetResponses(OpenApiOperation operation)
88+
{
89+
operation.Responses = new OpenApiResponses
90+
{
91+
{ Constants.StatusCode204, Constants.StatusCode204.GetResponse() },
92+
{ Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse() }
93+
};
94+
95+
base.SetResponses(operation);
96+
}
97+
98+
protected override void SetSecurity(OpenApiOperation operation)
99+
{
100+
UpdateRestrictionsType update = Context.Model.GetRecord<UpdateRestrictionsType>(EntitySet, CapabilitiesConstants.UpdateRestrictions);
101+
if (update == null || update.Permissions == null)
102+
{
103+
return;
104+
}
105+
106+
operation.Security = Context.CreateSecurityRequirements(update.Permissions).ToList();
107+
}
108+
109+
protected override void AppendCustomParameters(OpenApiOperation operation)
110+
{
111+
UpdateRestrictionsType update = Context.Model.GetRecord<UpdateRestrictionsType>(EntitySet, CapabilitiesConstants.UpdateRestrictions);
112+
if (update == null)
113+
{
114+
return;
115+
}
116+
117+
if (update.CustomHeaders != null)
118+
{
119+
AppendCustomParameters(operation, update.CustomHeaders, ParameterLocation.Header);
120+
}
121+
122+
if (update.CustomQueryOptions != null)
123+
{
124+
AppendCustomParameters(operation, update.CustomQueryOptions, ParameterLocation.Query);
125+
}
126+
}
127+
}
128+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ private readonly IDictionary<ODataPathKind, IDictionary<OperationType, IOperatio
2929
{
3030
{OperationType.Get, new EntityGetOperationHandler() },
3131
{OperationType.Patch, new EntityPatchOperationHandler() },
32+
{OperationType.Put, new EntityPutOperationHandler() },
3233
{OperationType.Delete, new EntityDeleteOperationHandler() }
3334
}},
3435

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ protected override void SetOperations(OpenApiPathItem item)
3232
UpdateRestrictionsType update = Context.Model.GetRecord<UpdateRestrictionsType>(EntitySet);
3333
if (update == null || update.IsUpdatable)
3434
{
35-
AddOperation(item, OperationType.Patch);
35+
if (update != null && update.IsUpdateMethodPut)
36+
{
37+
AddOperation(item, OperationType.Put);
38+
}
39+
else
40+
{
41+
AddOperation(item, OperationType.Patch);
42+
}
3643
}
3744

3845
DeleteRestrictionsType delete = Context.Model.GetRecord<DeleteRestrictionsType>(EntitySet);

src/Microsoft.OpenApi.OData.Reader/Vocabulary/Capabilities/UpdateRestrictionsType.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@
1111

1212
namespace Microsoft.OpenApi.OData.Vocabulary.Capabilities
1313
{
14+
/// <summary>
15+
/// Enumerates HTTP methods that can be used to update entities
16+
/// </summary>
17+
internal enum HttpMethod
18+
{
19+
/// <summary>
20+
/// The HTTP PATCH Method
21+
/// </summary>
22+
PATCH,
23+
24+
/// <summary>
25+
/// The HTTP PUT Method
26+
/// </summary>
27+
PUT
28+
}
1429
/// <summary>
1530
/// Complex Type: Org.OData.Capabilities.V1.UpdateRestrictionsType
1631
/// </summary>
@@ -33,6 +48,12 @@ internal class UpdateRestrictionsType : IRecord
3348
/// </summary>
3449
public bool? DeltaUpdateSupported { get; private set; }
3550

51+
/// <summary>
52+
/// Gets the value indicating the HTTP Method (PUT or PATCH) for updating an entity.
53+
/// If null, PATCH should be supported and PUT MAY be supported.
54+
/// </summary>
55+
public HttpMethod? UpdateMethod { get; private set; }
56+
3657
/// <summary>
3758
/// Gets the value indicating Members of collections can be updated via a PATCH request with a '/$filter(...)/$each' segment.
3859
/// </summary>
@@ -102,6 +123,11 @@ public bool IsNonUpdatableNavigationProperty(string navigationPropertyPath)
102123
false;
103124
}
104125

126+
/// <summary>
127+
/// Tests whether the update method for the entity has been explicitly specified as PUT
128+
/// </summary>
129+
public bool IsUpdateMethodPut => UpdateMethod.HasValue && UpdateMethod.Value == HttpMethod.PUT;
130+
105131
/// <summary>
106132
/// Init the <see cref="UpdateRestrictionsType"/>.
107133
/// </summary>
@@ -119,6 +145,9 @@ public void Initialize(IEdmRecordExpression record)
119145
// DeltaUpdateSupported
120146
DeltaUpdateSupported = record.GetBoolean("DeltaUpdateSupported");
121147

148+
// UpdateMethod
149+
UpdateMethod = record.GetEnum<HttpMethod>("UpdateMethod");
150+
122151
// FilterSegmentSupported
123152
FilterSegmentSupported = record.GetBoolean("FilterSegmentSupported");
124153

0 commit comments

Comments
 (0)