Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix issue 505 and add patch test cases, also support key as segment and add CORS header #507

Merged
merged 4 commits into from Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -220,7 +220,7 @@ private void SetValues(DbEntityEntry dbEntry, DataModificationItem item, Type re
propertyPair.Key));
}

value = Activator.CreateInstance(type);
value = propertyEntry.CurrentValue;
SetValues(value, type, dic);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK for this changes.
But add a TODO to line 257, the SetValues method is called by both patch and patch, we need to consider complex property has other complex type property cases.

Open an issue to track this and add to that TODO.

}

Expand Down
Expand Up @@ -155,6 +155,21 @@ protected void TestPostStatusCodeIs(string uriStringAfterServiceRoot, int status
Assert.Equal(statusCode, response.StatusCode);
}
}

protected async void TestPatchStatusCodeIs(string uriStringAfterServiceRoot, string patchContent, HttpStatusCode statusCode)
{
var requestUri = string.Format("{0}/{1}", this.ServiceBaseUri, uriStringAfterServiceRoot);
var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri);

request.Content = new StringContent(patchContent);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

HttpClient client = new HttpClient();
HttpResponseMessage response = await client.SendAsync(request);

Assert.Equal(statusCode, response.StatusCode);
}

#endregion

protected void ResetDataSource()
Expand Down
Expand Up @@ -80,6 +80,7 @@
<HintPath>..\..\..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.OData.Client, Version=6.15.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.OData.Client.6.15.0\lib\net40\Microsoft.OData.Client.dll</HintPath>
<Private>True</Private>
Expand All @@ -96,6 +97,10 @@
<HintPath>..\..\..\packages\Microsoft.Spatial.6.15.0\lib\portable-net45+win+wpa81\Microsoft.Spatial.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ServiceModel.Web" />
Expand Down
Expand Up @@ -5,9 +5,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.OData.Client;
using Microsoft.OData.Core;
using Microsoft.OData.Service.Sample.Trippin.Models;
using Newtonsoft.Json;
using Xunit;

namespace Microsoft.OData.Service.Sample.Tests
Expand Down Expand Up @@ -295,11 +297,11 @@ public void CURDComputedImmutableProperty()
// Query the updated entity
this.TestClientContext.Detach(employee);
employee = this.TestClientContext.Orders.Where(e => e.PersonId == personId && e.OrderId == orderId).First();

// both computed property and immutable property should not have new value
Assert.Equal(400, employee.Price);
Assert.NotEqual("ShouldBeIgnored2", employee.ComputedProperty);

// Immutable property has value set during insert.
Assert.NotEqual("ShouldBeIgnored2", employee.ImmutableProperty);
Assert.Equal("ShouldNotBeIgnored", employee.ImmutableProperty);
Expand Down Expand Up @@ -409,9 +411,9 @@ public void UQProperty()
Assert.Equal("Cooper", lastName);

// Update a property
Dictionary<string, string> headers = new Dictionary<string, string>()
Dictionary<string, string> headers = new Dictionary<string, string>()
{
{ "Content-Type", "application/json" }
{ "Content-Type", "application/json" }
};

HttpWebRequestMessage request = new HttpWebRequestMessage(
Expand Down Expand Up @@ -566,12 +568,12 @@ public void QueryOptions()
// skip
people2 = this.TestClientContext.People.Skip((int)(personId - 1)).ToList();
Assert.Equal(personId, people2.First().PersonId);

// count
var countQuery = this.TestClientContext.People.IncludeTotalCount().Skip(1).Take(2) as DataServiceQuery<Person>;
var response = countQuery.Execute() as QueryOperationResponse<Person>;
Assert.Equal(response.TotalCount, 14);

// count with expand
countQuery = this.TestClientContext.People.IncludeTotalCount().Expand("Friends").Skip(1).Take(2) as DataServiceQuery<Person>;
response = countQuery.Execute() as QueryOperationResponse<Person>;
Expand Down Expand Up @@ -638,7 +640,7 @@ public void FilterBuiltInDateFunctions()
Assert.True(flight1.All(f => f.StartsAt.Second == startDate.Second));

// Following built-in functions are not supported now.
// fractionalseconds
// fractionalseconds
// date
// time
// totaloffsetminutes
Expand Down Expand Up @@ -1119,5 +1121,32 @@ public void ConventionBasedChangeSetAuthorizerTest()
"The current user does not have permission to delete entities from the EntitySet 'Trips'.",
clientException.Message);
}

[Fact]
public void TestPatchSuccessfully()
{
// Get origin content.
var uriStringAfterServiceRoot = "Orders(PersonId=1, OrderId=1)";
var originContent = default(string);
Action<string> getContent = p => originContent = p;
TestGetPayload(uriStringAfterServiceRoot, getContent);

// Patch it.
var changedDescription = "TestDescription";
var changedNormalProperty = "TestNormalProperty";
string patchContent =
string.Format(
"{{\n \"Description\": \"{0}\",\n \"NormalOrderDetail\": {{\n \"NormalProperty\": \"{1}\"\n }}\n}}",
changedDescription,
changedNormalProperty);
TestPatchStatusCodeIs(uriStringAfterServiceRoot, patchContent, HttpStatusCode.NoContent);

// Test patch results.
dynamic content = JsonConvert.DeserializeObject(originContent);
content.Description = changedDescription;
content.NormalOrderDetail.NormalProperty = changedNormalProperty;
string changedContent = JsonConvert.SerializeObject(content);
TestGetPayloadContains(uriStringAfterServiceRoot, changedContent);
}
}
}
@@ -1,6 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Net;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Xunit;

namespace Microsoft.OData.Service.Sample.Tests
Expand Down Expand Up @@ -44,8 +48,8 @@ public class TrippinInMemoryE2ETest : TrippinInMemoryE2ETestBase
// TODO since webapi doesnot handle query with null, the trips here in the datasource are actually not null.
[InlineData("/People('clydeguess')/Trips", 200)]
// collection of navigation property's property and navigation property has null value
// TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. 500 is returned actually.
[InlineData("/People('willieashmore')/Friends/MiddleName", 500)]
// TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. (404 is returned when key-as-segment, otherwise, 500 will be returned.)
[InlineData("/People('willieashmore')/Friends/MiddleName", 404)]
public void QueryPropertyWithNullValueStatusCode(string url, int expectedCode)
{
TestGetStatusCodeIs(url, expectedCode);
Expand Down Expand Up @@ -77,8 +81,8 @@ public void QueryPropertyWithNullValueStatusCode(string url, int expectedCode)
// collection of navigation property
[InlineData("/People('NoneExist')/Friends", 404)]
// collection of navigation property's property
// TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. 500 is returned actually.
[InlineData("/People('NoneExist')/Friends/MiddleName", 500)]
// TODO should be bad request 400 as this is not allowed, 404 is returned by WebApi Route Match method. (404 is returned when key-as-segment, otherwise, 500 will be returned.)
[InlineData("/People('NoneExist')/Friends/MiddleName", 404)]
public void QueryPropertyWithNonExistEntity(string url, int expectedCode)
{
TestGetStatusCodeIs(url, expectedCode);
Expand Down Expand Up @@ -158,5 +162,46 @@ public void TestRawValuedEnumPropertyAccess()
{
TestGetPayloadIs("People('russellwhyte')/FavoriteFeature/$value", "Feature1");
}

[Fact]
public void TestPatchSuccessfully()
{
// Get origin content and sessionId.
var uriStringAfterServiceRoot = "Airports('KLAX')";
var originContent = default(string);
Action<string> getContent = p => originContent = p;
TestGetPayload(uriStringAfterServiceRoot, getContent);
var sessionId = GetSessionIdFromResponse(originContent);
Assert.NotNull(sessionId);

// Patch it.
uriStringAfterServiceRoot = string.Format(@"(S({0}))/{1}", sessionId, uriStringAfterServiceRoot);
var changedRegion = "TestRegion";
var changedAddress = "1 World Way, Los Angeles, CA, 90045";
string patchContent =
string.Format(
"{{\r\n \"Location\":{{\r\n \"Address\":\"{0}\",\r\n \"City\":{{\r\n \"Region\":\"{1}\"\r\n }}\r\n }}\r\n}}",
changedAddress,
changedRegion);
TestPatchStatusCodeIs(uriStringAfterServiceRoot, patchContent, HttpStatusCode.NoContent);

// Test patch results.
dynamic content = JsonConvert.DeserializeObject(originContent);
content.Location.Address = changedAddress;
content.Location.City.Region = changedRegion;
string changedContent = JsonConvert.SerializeObject(content);
TestGetPayloadContains(uriStringAfterServiceRoot, changedContent);
}

private static string GetSessionIdFromResponse(string response)
{
var match = Regex.Match(response, @"/\(S\((\w+)\)\)");
if (match.Success)
{
return match.Groups[1].Value;
}

return default(string);
}
}
}
Expand Up @@ -5,6 +5,7 @@
<package id="Microsoft.OData.Core" version="6.15.0" targetFramework="net45" />
<package id="Microsoft.OData.Edm" version="6.15.0" targetFramework="net45" />
<package id="Microsoft.Spatial" version="6.15.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="xunit" version="2.1.0" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
<package id="xunit.assert" version="2.1.0" targetFramework="net45" />
Expand Down
Expand Up @@ -16,6 +16,8 @@ public OrderDetail()

public string NormalProperty { get; set; }

public string AnotherNormalProperty { get; set; }

public string ComputedProperty { get; set; }

public string ImmutableProperty { get; set; }
Expand Down
Expand Up @@ -563,7 +563,11 @@ public static void ResetDataSource()
OrderId = 1,
Description = "Person 1 Order 1",
Price = 200,
NormalOrderDetail = new OrderDetail(),
NormalOrderDetail = new OrderDetail()
{
NormalProperty = "NormalProperty",
AnotherNormalProperty = "AnotherNormalProperty"
},
ComputedOrderDetail = new OrderDetail(),
ImmutableOrderDetail = new OrderDetail()
},
Expand Down
Expand Up @@ -3,6 +3,7 @@

using System.Web.Http;
using System.Web.OData;
using System.Web.OData.Extensions;
using Microsoft.OData.Service.Sample.TrippinInMemory.Api;
using Microsoft.Restier.Publishers.OData;
using Microsoft.Restier.Publishers.OData.Batch;
Expand All @@ -13,9 +14,10 @@ public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
RegisterTrippin(config, GlobalConfiguration.DefaultServer);
config.SetUseVerboseErrors(true);
config.MessageHandlers.Add(new ETagMessageHandler());
config.SetUrlKeyDelimiter(ODataUrlKeyDelimiter.Slash);
RegisterTrippin(config, GlobalConfiguration.DefaultServer);
}

public static async void RegisterTrippin(
Expand Down
Expand Up @@ -28,6 +28,12 @@
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
<uri>
<schemeSettings>
Expand Down
Expand Up @@ -202,7 +202,7 @@ private static void SetValues(object instance, Type type, IReadOnlyDictionary<st

if (dic != null)
{
value = Activator.CreateInstance(propertyInfo.PropertyType);
value = propertyInfo.GetValue(instance);
SetValues(value, propertyInfo.PropertyType, dic);
}
else if (propertyInfo.PropertyType.IsGenericType)
Expand Down