Skip to content

Commit a37da5b

Browse files
committed
issue #172 ETagMessageHandler is not supporting typeless entities
1 parent 0cac329 commit a37da5b

5 files changed

Lines changed: 197 additions & 3 deletions

File tree

OData/WebApiOData.E2E.sln

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 2013
4-
VisualStudioVersion = 12.0.31101.0
4+
VisualStudioVersion = 12.0.40629.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "base", "base", "{C015197B-27D3-499F-9A31-63E9E7D58F58}"
77
EndProject
@@ -28,6 +28,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.OData", "src\Sys
2828
EndProject
2929
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.OData", "src\System.Web.Http.OData\System.Web.Http.OData.csproj", "{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}"
3030
EndProject
31+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{2EB8D1F9-C505-4FA8-8D1C-F167B5B1AB8E}"
32+
ProjectSection(SolutionItems) = preProject
33+
.nuget\packages.config = .nuget\packages.config
34+
EndProjectSection
35+
EndProject
3136
Global
3237
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3338
CodeAnalysis|Any CPU = CodeAnalysis|Any CPU

OData/src/System.Web.OData/OData/ETagMessageHandler.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ private static IEdmEntityTypeReference GetTypeReference(IEdmModel model, IEdmEnt
8888
return null;
8989
}
9090

91+
IEdmObject edmObject = value as IEdmEntityObject;
92+
if (edmObject != null)
93+
{
94+
IEdmTypeReference edmTypeReference = edmObject.GetEdmType();
95+
return edmTypeReference.AsEntity();
96+
}
97+
9198
IEdmTypeReference reference = EdmLibHelpers.GetEdmTypeReference(model, value.GetType());
9299
if (reference != null && reference.Definition.IsOrInheritsFrom(edmType))
93100
{
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Linq;
5+
using System.Net.Http;
6+
using System.Web.Http;
7+
using System.Web.OData;
8+
using System.Web.OData.Extensions;
9+
using System.Web.OData.Routing;
10+
using System.Web.OData.Routing.Conventions;
11+
using Microsoft.OData.Core;
12+
using Microsoft.OData.Edm;
13+
using Microsoft.OData.Edm.Annotations;
14+
using Microsoft.OData.Edm.Expressions;
15+
using Microsoft.OData.Edm.Library;
16+
using Microsoft.OData.Edm.Vocabularies.V1;
17+
using Newtonsoft.Json.Linq;
18+
using Nuwa;
19+
using WebStack.QA.Test.OData.ModelBuilder;
20+
using Xunit;
21+
22+
namespace WebStack.QA.Test.OData.ETags
23+
{
24+
[NuwaFramework]
25+
public class ETagsUntypedTests
26+
{
27+
[NuwaBaseAddress]
28+
public string BaseAddress { get; set; }
29+
30+
[NuwaHttpClient]
31+
public HttpClient Client { get; set; }
32+
33+
[NuwaConfiguration]
34+
public static void UpdateConfiguration(HttpConfiguration configuration)
35+
{
36+
configuration.Routes.Clear();
37+
configuration.MapODataServiceRoute("odata", "odata", GetEdmModel(), new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault());
38+
configuration.MessageHandlers.Add(new ETagMessageHandler());
39+
}
40+
41+
private static IEdmModel GetEdmModel()
42+
{
43+
EdmModel model = new EdmModel();
44+
45+
// entity type customer
46+
EdmEntityType customer = new EdmEntityType("NS", "Customer");
47+
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
48+
IEdmStructuralProperty customerName = customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);
49+
model.AddElement(customer);
50+
51+
// entity sets
52+
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
53+
model.AddElement(container);
54+
EdmEntitySet customers = container.AddEntitySet("ETagUntypedCustomers", customer);
55+
56+
model.SetOptimisticConcurrencyAnnotation(customers, new[] { customerName });
57+
58+
return model;
59+
}
60+
61+
[Fact]
62+
public void ModelBuilderTest()
63+
{
64+
const string expectMetadata =
65+
" <EntitySet Name=\"ETagUntypedCustomers\" EntityType=\"NS.Customer\">\r\n" +
66+
" <Annotation Term=\"Org.OData.Core.V1.OptimisticConcurrency\">\r\n" +
67+
" <Collection>\r\n" +
68+
" <PropertyPath>Name</PropertyPath>\r\n" +
69+
" </Collection>\r\n" +
70+
" </Annotation>\r\n" +
71+
" </EntitySet>";
72+
73+
string requestUri = string.Format("{0}/odata/$metadata", this.BaseAddress);
74+
75+
HttpResponseMessage response = this.Client.GetAsync(requestUri).Result;
76+
77+
var content = response.Content.ReadAsStringAsync().Result;
78+
Assert.Contains(expectMetadata, content);
79+
80+
var stream = response.Content.ReadAsStreamAsync().Result;
81+
IODataResponseMessage message = new ODataMessageWrapper(stream, response.Content.Headers);
82+
var reader = new ODataMessageReader(message);
83+
var edmModel = reader.ReadMetadataDocument();
84+
Assert.NotNull(edmModel);
85+
86+
var etagCustomers = edmModel.FindDeclaredEntitySet("ETagUntypedCustomers");
87+
Assert.NotNull(etagCustomers);
88+
89+
var annotations = edmModel.FindDeclaredVocabularyAnnotations(etagCustomers);
90+
IEdmVocabularyAnnotation annotation = Assert.Single(annotations);
91+
Assert.NotNull(annotation);
92+
93+
Assert.Same(CoreVocabularyModel.ConcurrencyTerm, annotation.Term);
94+
Assert.Same(etagCustomers, annotation.Target);
95+
96+
IEdmValueAnnotation valueAnnotation = annotation as IEdmValueAnnotation;
97+
Assert.NotNull(valueAnnotation);
98+
Assert.NotNull(valueAnnotation.Value);
99+
100+
IEdmCollectionExpression collection = valueAnnotation.Value as IEdmCollectionExpression;
101+
Assert.NotNull(collection);
102+
Assert.Equal(new[] { "Name" }, collection.Elements.Select(e => ((IEdmPathExpression) e).Path.Single()));
103+
}
104+
105+
[Fact]
106+
public void PatchUpdatedEntryWithIfMatchShouldReturnPreconditionFailed()
107+
{
108+
string requestUri = this.BaseAddress + "/odata/ETagUntypedCustomers(1)?$format=json";
109+
110+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
111+
HttpResponseMessage response = this.Client.SendAsync(request).Result;
112+
113+
Assert.True(response.IsSuccessStatusCode);
114+
var etagInHeader = response.Headers.ETag.ToString();
115+
JObject result = response.Content.ReadAsAsync<JObject>().Result;
116+
var etagInPayload = (string)result["@odata.etag"];
117+
118+
Assert.True(etagInPayload == etagInHeader);
119+
Assert.Equal(etagInPayload, "W/\"J1NhbSc=\"");
120+
}
121+
}
122+
123+
public class ETagUntypedCustomersController : ODataController
124+
{
125+
[EnableQuery]
126+
public IHttpActionResult Get(int key)
127+
{
128+
IEdmModel model = Request.ODataProperties().Model;
129+
IEdmEntityType entityType = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Customer");
130+
EdmEntityObject customer = new EdmEntityObject(entityType);
131+
customer.TrySetPropertyValue("ID", key);
132+
customer.TrySetPropertyValue("Name", "Sam");
133+
return Ok(customer);
134+
}
135+
}
136+
}

OData/test/E2ETest/WebStack.QA.Test.OData/WebStack.QA.Test.OData.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
<Compile Include="ETags\ETagCurrencyTokenEfContext.cs" />
171171
<Compile Include="ETags\ETagCurrencyTokenEfContextTest.cs" />
172172
<Compile Include="ETags\ETagsOtherTypesTest.cs" />
173+
<Compile Include="ETags\ETagsUntypedTests.cs" />
173174
<Compile Include="Formatter\ODataFeedSerializeWithoutNavigationSourceTests.cs" />
174175
<Compile Include="ODataCountTest\CountController.cs" />
175176
<Compile Include="ODataCountTest\CountDataModel.cs" />

OData/test/UnitTest/System.Web.OData.Test/OData/ETagMessageHandlerTest.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.ComponentModel.DataAnnotations;
6+
using System.Linq;
67
using System.Net;
78
using System.Net.Http;
89
using System.Net.Http.Formatting;
@@ -12,7 +13,9 @@
1213
using System.Web.OData.Builder;
1314
using System.Web.OData.Extensions;
1415
using System.Web.OData.Routing;
16+
using System.Web.SessionState;
1517
using Microsoft.OData.Edm;
18+
using Microsoft.OData.Edm.Library;
1619
using Microsoft.TestCommon;
1720
using Moq;
1821

@@ -189,14 +192,37 @@ public void SendAsync_WritesETagToResponseHeaders()
189192
Assert.NotNull(response.Headers.ETag);
190193
}
191194

192-
private static HttpRequestMessage SetupRequest(HttpMethod method, string odataPath)
195+
[Fact]
196+
public void SendAsync_WritesETagToResponseHeaders_InUntyped()
197+
{
198+
// Arrange
199+
IEdmModel model = GetUnTypeEdmModel();
200+
HttpRequestMessage request = SetupRequest(HttpMethod.Get, "Customers(3)", model);
201+
202+
IEdmEntityType entityType = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Customer");
203+
EdmEntityObject customer = new EdmEntityObject(entityType);
204+
customer.TrySetPropertyValue("ID", 3);
205+
customer.TrySetPropertyValue("Name", "Sam");
206+
207+
HttpResponseMessage originalResponse = SetupResponse(HttpStatusCode.OK, typeof(EdmEntityObject), customer);
208+
ETagMessageHandler handler = new ETagMessageHandler() { InnerHandler = new TestHandler(originalResponse) };
209+
210+
// Act
211+
HttpResponseMessage response = handler.SendAsync(request).Result;
212+
213+
// Assert
214+
Assert.NotNull(response.Headers.ETag);
215+
Assert.Equal("\"J1NhbSc=\"", response.Headers.ETag.Tag);
216+
}
217+
218+
private static HttpRequestMessage SetupRequest(HttpMethod method, string odataPath, IEdmModel edmModel = null)
193219
{
194220
HttpRequestMessage request;
195221
HttpConfiguration configuration = new HttpConfiguration();
196222
request = new HttpRequestMessage(method, "http://host/any");
197223
request.SetConfiguration(configuration);
198224
HttpRequestMessageProperties properties = request.ODataProperties();
199-
IEdmModel model = SetupModel();
225+
IEdmModel model = edmModel ?? SetupModel();
200226
properties.Model = model;
201227
properties.PathHandler = new DefaultODataPathHandler();
202228
properties.Path = properties.PathHandler.Parse(model, "http://localhost/any", odataPath);
@@ -236,6 +262,25 @@ private static IEdmModel SetupModel()
236262
return model;
237263
}
238264

265+
private static IEdmModel GetUnTypeEdmModel()
266+
{
267+
EdmModel model = new EdmModel();
268+
269+
// entity type customer
270+
EdmEntityType customer = new EdmEntityType("NS", "Customer");
271+
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
272+
IEdmStructuralProperty customerName = customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);
273+
model.AddElement(customer);
274+
275+
// entity sets
276+
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
277+
model.AddElement(container);
278+
EdmEntitySet customers = container.AddEntitySet("Customers", customer);
279+
280+
model.SetOptimisticConcurrencyAnnotation(customers, new[] { customerName });
281+
return model;
282+
}
283+
239284
public class ETagCustomer
240285
{
241286
public int Id { get; set; }

0 commit comments

Comments
 (0)