diff --git a/.gitignore b/.gitignore
index fd5204b5..d9edd238 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.suo
*.user
*.sln.docstates
+.vs/
# Build results
[Dd]ebug/
diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config
deleted file mode 100644
index bc28afba..00000000
--- a/.vs/config/applicationhost.config
+++ /dev/null
@@ -1,1046 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/AcceptanceTestsBase.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/AcceptanceTestsBase.cs
index 40aa3945..d0594337 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/AcceptanceTestsBase.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/AcceptanceTestsBase.cs
@@ -1,7 +1,10 @@
using System;
using System.Data.Common;
+using System.Globalization;
+using System.IO;
using System.Net;
using System.Net.Http;
+using System.Net.Http.Formatting;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -10,6 +13,10 @@
using JSONAPI.Json;
using Microsoft.Owin.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+using Owin;
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
@@ -20,37 +27,58 @@ public abstract class AcceptanceTestsBase
private static readonly Regex GuidRegex = new Regex(@"\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b", RegexOptions.IgnoreCase);
//private static readonly Regex StackTraceRegex = new Regex(@"""stackTrace"":[\s]*""[\w\:\\\.\s\,\-]*""");
private static readonly Regex StackTraceRegex = new Regex(@"""stackTrace""[\s]*:[\s]*"".*?""");
- private static readonly Uri BaseUri = new Uri("https://www.example.com");
+ protected static Uri BaseUri = new Uri("https://www.example.com");
protected static DbConnection GetEffortConnection()
{
return TestHelpers.GetEffortConnection(@"Data");
}
- protected static async Task AssertResponseContent(HttpResponseMessage response, string expectedResponseTextResourcePath, HttpStatusCode expectedStatusCode, bool redactErrorData = false)
+ protected virtual async Task AssertResponseContent(HttpResponseMessage response, string expectedResponseTextResourcePath, HttpStatusCode expectedStatusCode, bool redactErrorData = false)
{
var responseContent = await response.Content.ReadAsStringAsync();
- var expectedResponse =
- JsonHelpers.MinifyJson(TestHelpers.ReadEmbeddedFile(expectedResponseTextResourcePath));
+ var expectedResponse = ExpectedResponse(expectedResponseTextResourcePath);
string actualResponse;
if (redactErrorData)
{
var redactedResponse = GuidRegex.Replace(responseContent, "{{SOME_GUID}}");
actualResponse = StackTraceRegex.Replace(redactedResponse, "\"stackTrace\":\"{{STACK_TRACE}}\"");
+ actualResponse.Should().Be(expectedResponse);
}
else
{
actualResponse = responseContent;
+ JsonSerializerSettings settings = new JsonSerializerSettings
+ {
+ DateTimeZoneHandling = DateTimeZoneHandling.Utc,
+ DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffff+00:00",
+ Culture = CultureInfo.InvariantCulture,
+ Formatting = Formatting.Indented
+ };
+
+ var actualResponseJObject = JsonConvert.DeserializeObject(actualResponse) as JObject;
+ var expectedResponseJObject = JsonConvert.DeserializeObject(expectedResponse) as JObject;
+ var equals = JToken.DeepEquals(actualResponseJObject, expectedResponseJObject);
+ if (!equals)
+ {
+ Assert.Fail("should be: " + JsonConvert.SerializeObject(expectedResponseJObject, settings) + "\n but was: " + JsonConvert.SerializeObject(actualResponseJObject, settings));
+ }
}
- actualResponse.Should().Be(expectedResponse);
response.Content.Headers.ContentType.MediaType.Should().Be(JsonApiContentType);
response.Content.Headers.ContentType.CharSet.Should().Be("utf-8");
response.StatusCode.Should().Be(expectedStatusCode);
}
+ protected virtual string ExpectedResponse(string expectedResponseTextResourcePath)
+ {
+ var expectedResponse =
+ JsonHelpers.MinifyJson(TestHelpers.ReadEmbeddedFile(expectedResponseTextResourcePath));
+ return expectedResponse;
+ }
+
#region GET
protected async Task SubmitGet(DbConnection effortConnection, string requestPath)
@@ -58,7 +86,7 @@ protected async Task SubmitGet(DbConnection effortConnectio
using (var server = TestServer.Create(app =>
{
var startup = new Startup(() => new TestDbContext(effortConnection, false));
- startup.Configuration(app);
+ StartupConfiguration(startup, app);
}))
{
var uri = new Uri(BaseUri, requestPath);
@@ -75,7 +103,7 @@ protected async Task SubmitPost(DbConnection effortConnecti
using (var server = TestServer.Create(app =>
{
var startup = new Startup(() => new TestDbContext(effortConnection, false));
- startup.Configuration(app);
+ StartupConfiguration(startup, app);
}))
{
var uri = new Uri(BaseUri, requestPath);
@@ -100,7 +128,7 @@ protected async Task SubmitPatch(DbConnection effortConnect
using (var server = TestServer.Create(app =>
{
var startup = new Startup(() => new TestDbContext(effortConnection, false));
- startup.Configuration(app);
+ StartupConfiguration(startup, app);
}))
{
var uri = new Uri(BaseUri, requestPath);
@@ -124,7 +152,7 @@ protected async Task SubmitDelete(DbConnection effortConnec
using (var server = TestServer.Create(app =>
{
var startup = new Startup(() => new TestDbContext(effortConnection, false));
- startup.Configuration(app);
+ StartupConfiguration(startup, app);
}))
{
var uri = new Uri(BaseUri, requestPath);
@@ -137,5 +165,22 @@ protected async Task SubmitDelete(DbConnection effortConnec
}
#endregion
+
+
+
+ #region configure startup
+
+ ///
+ /// Startup process was divided into 4 steps to support better acceptance tests.
+ /// This method can be overridden by subclass to change behavior of setup.
+ ///
+ ///
+ ///
+ protected virtual void StartupConfiguration(Startup startup, IAppBuilder app)
+ {
+ startup.Configuration(app);
+ }
+
+ #endregion
}
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/BaseUrlTest.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/BaseUrlTest.cs
new file mode 100644
index 00000000..cda8ccf8
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/BaseUrlTest.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Data.SqlTypes;
+using System.Linq;
+using System.Net;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using FluentAssertions;
+using JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models;
+using JSONAPI.Http;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Owin;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
+{
+ [TestClass]
+ public class BaseUrlTest : AcceptanceTestsBase
+ {
+ [TestInitialize]
+ public void TestInit()
+ {
+ if (!BaseUri.AbsoluteUri.EndsWith("api/"))
+ {
+ BaseUri = new Uri(BaseUri.AbsoluteUri + "api/");
+ }
+ }
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ if (BaseUri.AbsoluteUri.EndsWith("api/"))
+ {
+ BaseUri = new Uri(BaseUri.AbsoluteUri.Substring(0,BaseUri.AbsoluteUri.Length -4));
+ }
+ }
+
+ // custom startup process for this test
+ protected override void StartupConfiguration(Startup startup, IAppBuilder app)
+ {
+
+ var configuration = startup.BuildConfiguration();
+ // here we add the custom BaseUrlServcie
+ configuration.CustomBaseUrlService = new BaseUrlService("api");
+ var configurator = startup.BuildAutofacConfigurator(app);
+ var httpConfig = startup.BuildHttpConfiguration();
+ startup.MergeAndSetupConfiguration(app, configurator, httpConfig, configuration);
+ }
+
+ // custom expected response method
+ protected override string ExpectedResponse(string expectedResponseTextResourcePath)
+ {
+ var expected = base.ExpectedResponse(expectedResponseTextResourcePath);
+ return Regex.Replace(expected, @"www\.example\.com\/", @"www.example.com/api/");
+ }
+
+ // copied some tests in here
+
+ // copied from ComputedIdTests
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Language.csv", @"Data")]
+ [DeploymentItem(@"Data\LanguageUserLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task Get_resource_with_computed_id_by_id()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "language-user-links/9001_402");
+
+ await AssertResponseContent(response, @"Fixtures\ComputedId\Responses\Get_resource_with_computed_id_by_id_Response.json", HttpStatusCode.OK);
+ }
+ }
+
+
+ // copied from CreatingResourcesTests
+
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostLongId.csv", @"Data")]
+ public async Task PostLongId_with_client_provided_id()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPost(effortConnection, "post-long-ids", @"Fixtures\CreatingResources\Requests\PostLongId_with_client_provided_id_Request.json");
+
+ await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\PostLongId_with_client_provided_id_Response.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsLongId.ToArray();
+ allPosts.Length.Should().Be(5);
+ var actualPost = allPosts.First(t => t.Id == 205);
+ actualPost.Id.Should().Be(205);
+ actualPost.Title.Should().Be("Added post");
+ actualPost.Content.Should().Be("Added post content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 03, 11, 04, 31, 0, new TimeSpan(0)));
+ }
+ }
+ }
+
+
+
+ // copied from DeletingResourcesTests
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostID.csv", @"Data")]
+ public async Task DeleteID()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitDelete(effortConnection, "post-i-ds/203");
+
+ var responseContent = await response.Content.ReadAsStringAsync();
+ responseContent.Should().Be("");
+ response.StatusCode.Should().Be(HttpStatusCode.NoContent);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsID.ToArray();
+ allPosts.Length.Should().Be(3);
+ var actualPosts = allPosts.FirstOrDefault(t => t.ID == "203");
+ actualPosts.Should().BeNull();
+ }
+ }
+ }
+
+
+
+ // copied from FetchingResourcesTests
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetWithFilter()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "posts?filter[title]=Post 4");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\GetWithFilterResponse.json", HttpStatusCode.OK);
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetById()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "posts/202");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\GetByIdResponse.json", HttpStatusCode.OK);
+ }
+ }
+
+ }
+}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/CreatingResourcesTests.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/CreatingResourcesTests.cs
index 40a973df..b189e933 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/CreatingResourcesTests.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/CreatingResourcesTests.cs
@@ -39,6 +39,52 @@ public async Task Post_with_client_provided_id()
}
}
+ [TestMethod]
+ [DeploymentItem(@"Data\PostID.csv", @"Data")]
+ public async Task PostID_with_client_provided_id()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPost(effortConnection, "post-i-ds", @"Fixtures\CreatingResources\Requests\PostID_with_client_provided_id_Request.json");
+
+ await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\PostID_with_client_provided_id_Response.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsID.ToArray();
+ allPosts.Length.Should().Be(5);
+ var actualPost = allPosts.First(t => t.ID == "205");
+ actualPost.ID.Should().Be("205");
+ actualPost.Title.Should().Be("Added post");
+ actualPost.Content.Should().Be("Added post content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 03, 11, 04, 31, 0, new TimeSpan(0)));
+ }
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostLongId.csv", @"Data")]
+ public async Task PostLongId_with_client_provided_id()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPost(effortConnection, "post-long-ids", @"Fixtures\CreatingResources\Requests\PostLongId_with_client_provided_id_Request.json");
+
+ await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\PostLongId_with_client_provided_id_Response.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsLongId.ToArray();
+ allPosts.Length.Should().Be(5);
+ var actualPost = allPosts.First(t => t.Id == 205);
+ actualPost.Id.Should().Be(205);
+ actualPost.Title.Should().Be("Added post");
+ actualPost.Content.Should().Be("Added post content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 03, 11, 04, 31, 0, new TimeSpan(0)));
+ }
+ }
+ }
+
[TestMethod]
[DeploymentItem(@"Data\Comment.csv", @"Data")]
[DeploymentItem(@"Data\Post.csv", @"Data")]
@@ -66,5 +112,34 @@ public async Task Post_with_empty_id()
}
}
}
+
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task Post_with_empty_id_and_include()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPost(effortConnection, "posts?include=author", @"Fixtures\CreatingResources\Requests\Post_with_empty_id_Request.json");
+
+ await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\Post_with_empty_id_and_include_author_Response.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.Posts.ToArray();
+ allPosts.Length.Should().Be(5);
+ var actualPost = allPosts.First(t => t.Id == "230");
+ actualPost.Id.Should().Be("230");
+ actualPost.Title.Should().Be("New post");
+ actualPost.Content.Should().Be("The server generated my ID");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 04, 13, 12, 09, 0, new TimeSpan(0, 3, 0, 0)));
+ actualPost.AuthorId.Should().Be("401");
+ }
+ }
+ }
}
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Child.csv b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Child.csv
new file mode 100644
index 00000000..b529aab6
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Child.csv
@@ -0,0 +1,7 @@
+Id,ChildDescription,MasterId
+7500,"Child 1 Description",1500
+7501,"Child 2 Description",1501
+7502,"Child 3 Description",1501
+7503,"Child 4 Description",1503
+7504,"Child 5 Description",1503
+7505,"Child 6 Description",1503
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Master.csv b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Master.csv
new file mode 100644
index 00000000..2a582cc4
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/Master.csv
@@ -0,0 +1,5 @@
+Id,Description
+1500,"Master 1 Description"
+1501,"Master 2 Description"
+1502,"Master 3 Description"
+1503,"Master 4 Description"
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostID.csv b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostID.csv
new file mode 100644
index 00000000..544b39c7
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostID.csv
@@ -0,0 +1,5 @@
+ID,Title,Content,Created,AuthorId
+"201","Post 1","Post 1 content","2015-01-31T14:00Z"
+"202","Post 2","Post 2 content","2015-02-05T08:10Z"
+"203","Post 3","Post 3 content","2015-02-07T11:11Z"
+"204","Post 4","Post 4 content","2015-02-08T06:59Z"
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostLongId.csv b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostLongId.csv
new file mode 100644
index 00000000..ac3f5a42
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Data/PostLongId.csv
@@ -0,0 +1,5 @@
+Id,Title,Content,Created,AuthorId
+201,"Post 1","Post 1 content","2015-01-31T14:00Z"
+202,"Post 2","Post 2 content","2015-02-05T08:10Z"
+203,"Post 3","Post 3 content","2015-02-07T11:11Z"
+204,"Post 4","Post 4 content","2015-02-08T06:59Z"
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/DeletingResourcesTests.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/DeletingResourcesTests.cs
index c5902256..f77715f5 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/DeletingResourcesTests.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/DeletingResourcesTests.cs
@@ -11,11 +11,11 @@ namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
public class DeletingResourcesTests : AcceptanceTestsBase
{
[TestMethod]
- [DeploymentItem(@"Data\Comment.csv", @"Acceptance\Data")]
- [DeploymentItem(@"Data\Post.csv", @"Acceptance\Data")]
- [DeploymentItem(@"Data\PostTagLink.csv", @"Acceptance\Data")]
- [DeploymentItem(@"Data\Tag.csv", @"Acceptance\Data")]
- [DeploymentItem(@"Data\User.csv", @"Acceptance\Data")]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
public async Task Delete()
{
using (var effortConnection = GetEffortConnection())
@@ -28,10 +28,54 @@ public async Task Delete()
using (var dbContext = new TestDbContext(effortConnection, false))
{
- var allTodos = dbContext.Posts.ToArray();
- allTodos.Length.Should().Be(3);
- var actualTodo = allTodos.FirstOrDefault(t => t.Id == "203");
- actualTodo.Should().BeNull();
+ var allPosts = dbContext.Posts.ToArray();
+ allPosts.Length.Should().Be(3);
+ var actualPosts = allPosts.FirstOrDefault(t => t.Id == "203");
+ actualPosts.Should().BeNull();
+ }
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostID.csv", @"Data")]
+ public async Task DeleteID()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitDelete(effortConnection, "post-i-ds/203");
+
+ var responseContent = await response.Content.ReadAsStringAsync();
+ responseContent.Should().Be("");
+ response.StatusCode.Should().Be(HttpStatusCode.NoContent);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsID.ToArray();
+ allPosts.Length.Should().Be(3);
+ var actualPosts = allPosts.FirstOrDefault(t => t.ID == "203");
+ actualPosts.Should().BeNull();
+ }
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostLongId.csv", @"Data")]
+ public async Task DeleteLongId()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitDelete(effortConnection, "post-long-ids/203");
+
+ var responseContent = await response.Content.ReadAsStringAsync();
+ responseContent.Should().Be("");
+ response.StatusCode.Should().Be(HttpStatusCode.NoContent);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsLongId.ToArray();
+ allPosts.Length.Should().Be(3);
+ var actualPosts = allPosts.FirstOrDefault(t => t.Id == 203);
+ actualPosts.Should().BeNull();
}
}
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/FetchingResourcesTests.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/FetchingResourcesTests.cs
index 5ec6cc98..39eb2938 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/FetchingResourcesTests.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/FetchingResourcesTests.cs
@@ -99,6 +99,40 @@ public async Task Get_related_to_many()
}
}
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task Get_related_to_many_included()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "posts/201/comments?include=author");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\Get_related_to_many_include_response.json", HttpStatusCode.OK);
+ }
+ }
+
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task Get_related_to_many_included_external()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "users/401/posts?include=tags");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\Get_related_to_many_include_external_response.json", HttpStatusCode.OK);
+ }
+ }
+
[TestMethod]
[DeploymentItem(@"Data\Comment.csv", @"Data")]
[DeploymentItem(@"Data\Post.csv", @"Data")]
@@ -115,6 +149,23 @@ public async Task Get_related_to_one()
}
}
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task Get_included_to_one()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "posts/201?include=author");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\Get_included_to_one_response.json", HttpStatusCode.OK);
+ }
+ }
+
[TestMethod]
[DeploymentItem(@"Data\Comment.csv", @"Data")]
[DeploymentItem(@"Data\Post.csv", @"Data")]
@@ -183,5 +234,18 @@ await AssertResponseContent(response,
HttpStatusCode.OK);
}
}
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Master.csv", @"Data")]
+ [DeploymentItem(@"Data\Child.csv", @"Data")]
+ public async Task Get_related_to_many_integer_key()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "masters/1501/children");
+
+ await AssertResponseContent(response, @"Fixtures\FetchingResources\Get_related_to_many_integer_key_response.json", HttpStatusCode.OK);
+ }
+ }
}
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/AttributeSerialization/Attributes_of_various_types_serialize_correctly.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/AttributeSerialization/Attributes_of_various_types_serialize_correctly.json
index ba0978cf..ca94936a 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/AttributeSerialization/Attributes_of_various_types_serialize_correctly.json
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/AttributeSerialization/Attributes_of_various_types_serialize_correctly.json
@@ -1,89 +1,110 @@
{
- "data": [
- {
- "type": "samples",
- "id": "1",
- "attributes": {
- "boolean-field": false,
- "byte-field": 0,
- "complex-attribute-field": null,
- "date-time-field": "0001-01-01T00:00:00",
- "date-time-offset-field": "0001-01-01T00:00:00.0000000+00:00",
- "decimal-field": "0",
- "double-field": 0.0,
- "enum-field": 0,
- "guid-field": "00000000-0000-0000-0000-000000000000",
- "int16-field": 0,
- "int32-field": 0,
- "int64-field": 0,
- "nullable-boolean-field": false,
- "nullable-byte-field": null,
- "nullable-date-time-field": null,
- "nullable-date-time-offset-field": null,
- "nullable-decimal-field": null,
- "nullable-double-field": null,
- "nullable-enum-field": null,
- "nullable-guid-field": null,
- "nullable-int16-field": null,
- "nullable-int32-field": null,
- "nullable-int64-field": null,
- "nullable-sbyte-field": null,
- "nullable-single-field": null,
- "nullable-uint16-field": null,
- "nullable-uint32-field": null,
- "nullable-uint64-field": null,
- "sbyte-field": 0,
- "single-field": 0.0,
- "string-field": null,
- "uint16-field": 0,
- "uint32-field": 0,
- "uint64-field": 0
- }
+ "data": [
+ {
+ "type": "samples",
+ "id": "1",
+ "attributes": {
+ "boolean-field": false,
+ "byte-field": 0,
+ "complex-attribute-field": null,
+ "date-time-field": "0001-01-01T00:00:00",
+ "date-time-offset-field": "0001-01-01T00:00:00.0000000+00:00",
+ "decimal-field": "0",
+ "double-field": 0.0,
+ "enum-field": 0,
+ "guid-field": "00000000-0000-0000-0000-000000000000",
+ "int16-field": 0,
+ "int32-field": 0,
+ "int64-field": 0,
+ "j-token-array-field": null,
+ "j-token-object-field": null,
+ "j-token-string-field": null,
+ "nullable-boolean-field": false,
+ "nullable-byte-field": null,
+ "nullable-date-time-field": null,
+ "nullable-date-time-offset-field": null,
+ "nullable-decimal-field": null,
+ "nullable-double-field": null,
+ "nullable-enum-field": null,
+ "nullable-guid-field": null,
+ "nullable-int16-field": null,
+ "nullable-int32-field": null,
+ "nullable-int64-field": null,
+ "nullable-sbyte-field": null,
+ "nullable-single-field": null,
+ "nullable-uint16-field": null,
+ "nullable-uint32-field": null,
+ "nullable-uint64-field": null,
+ "sbyte-field": 0,
+ "single-field": 0.0,
+ "string-field": null,
+ "uint16-field": 0,
+ "uint32-field": 0,
+ "uint64-field": 0
+ }
+ },
+ {
+ "type": "samples",
+ "id": "2",
+ "attributes": {
+ "boolean-field": true,
+ "byte-field": 253,
+ "complex-attribute-field": {
+ "foo": {
+ "baz": [ 11 ]
+ },
+ "bar": 5
},
- {
- "type": "samples",
- "id": "2",
- "attributes": {
- "boolean-field": true,
- "byte-field": 253,
- "complex-attribute-field": {
- "foo": {
- "baz": [ 11 ]
- },
- "bar": 5
- },
- "date-time-field": "1776-07-04T00:00:00",
- "date-time-offset-field": "1776-07-04T00:00:00.0000000-05:00",
- "decimal-field": "1056789.123",
- "double-field": 1056789.123,
- "enum-field": 1,
- "guid-field": "6566f9b4-5245-40de-890d-98b40a4ad656",
- "int16-field": 32000,
- "int32-field": 2000000000,
- "int64-field": 9223372036854775807,
- "nullable-boolean-field": true,
- "nullable-byte-field": 253,
- "nullable-date-time-field": "1776-07-04T00:00:00",
- "nullable-date-time-offset-field": "1776-07-04T00:00:00.0000000-05:00",
- "nullable-decimal-field": "1056789.123",
- "nullable-double-field": 1056789.123,
- "nullable-enum-field": 2,
- "nullable-guid-field": "3d1fb81e-43ee-4d04-af91-c8a326341293",
- "nullable-int16-field": 32000,
- "nullable-int32-field": 2000000000,
- "nullable-int64-field": 9223372036854775807,
- "nullable-sbyte-field": 123,
- "nullable-single-field": 1056789.13,
- "nullable-uint16-field": 64000,
- "nullable-uint32-field": 3000000000,
- "nullable-uint64-field": 9223372036854775808,
- "sbyte-field": 123,
- "single-field": 1056789.13,
- "string-field": "Some string 156",
- "uint16-field": 64000,
- "uint32-field": 3000000000,
- "uint64-field": 9223372036854775808
- }
- }
- ]
+ "date-time-field": "1776-07-04T00:00:00",
+ "date-time-offset-field": "1776-07-04T00:00:00.0000000-05:00",
+ "decimal-field": "1056789.123",
+ "double-field": 1056789.123,
+ "enum-field": 1,
+ "guid-field": "6566f9b4-5245-40de-890d-98b40a4ad656",
+ "int16-field": 32000,
+ "int32-field": 2000000000,
+ "int64-field": 9223372036854775807,
+ "j-token-array-field": [
+ {
+ "my-field1": "George Washington",
+ "overridden-field2": null,
+ "MyField3": 216
+ },
+ {
+ "my-field1": "Thomas Jefferson",
+ "overridden-field2": false,
+ "MyField3": 631
+ }
+ ],
+ "j-token-object-field": {
+ "my-field1": "Abraham Lincoln",
+ "overridden-field2": true,
+ "MyField3": 439
+ },
+ "j-token-string-field": "Andrew Jackson",
+ "nullable-boolean-field": true,
+ "nullable-byte-field": 253,
+ "nullable-date-time-field": "1776-07-04T00:00:00",
+ "nullable-date-time-offset-field": "1776-07-04T00:00:00.0000000-05:00",
+ "nullable-decimal-field": "1056789.123",
+ "nullable-double-field": 1056789.123,
+ "nullable-enum-field": 2,
+ "nullable-guid-field": "3d1fb81e-43ee-4d04-af91-c8a326341293",
+ "nullable-int16-field": 32000,
+ "nullable-int32-field": 2000000000,
+ "nullable-int64-field": 9223372036854775807,
+ "nullable-sbyte-field": 123,
+ "nullable-single-field": 1056789.13,
+ "nullable-uint16-field": 64000,
+ "nullable-uint32-field": 3000000000,
+ "nullable-uint64-field": 9223372036854775808,
+ "sbyte-field": 123,
+ "single-field": 1056789.13,
+ "string-field": "Some string 156",
+ "uint16-field": 64000,
+ "uint32-field": 3000000000,
+ "uint64-field": 9223372036854775808
+ }
+ }
+ ]
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostID_with_client_provided_id_Request.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostID_with_client_provided_id_Request.json
new file mode 100644
index 00000000..41424e02
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostID_with_client_provided_id_Request.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-i-ds",
+ "id": "205",
+ "attributes": {
+ "title": "Added post",
+ "content": "Added post content",
+ "created": "2015-03-11T04:31:00+00:00"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostLongId_with_client_provided_id_Request.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostLongId_with_client_provided_id_Request.json
new file mode 100644
index 00000000..b52fe5c4
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Requests/PostLongId_with_client_provided_id_Request.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-long-ids",
+ "id": "205",
+ "attributes": {
+ "title": "Added post",
+ "content": "Added post content",
+ "created": "2015-03-11T04:31:00+00:00"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostID_with_client_provided_id_Response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostID_with_client_provided_id_Response.json
new file mode 100644
index 00000000..7122e9e9
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostID_with_client_provided_id_Response.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-i-ds",
+ "id": "205",
+ "attributes": {
+ "content": "Added post content",
+ "created": "2015-03-11T04:31:00.0000000+00:00",
+ "title": "Added post"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostLongId_with_client_provided_id_Response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostLongId_with_client_provided_id_Response.json
new file mode 100644
index 00000000..dc5ee0f8
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/PostLongId_with_client_provided_id_Response.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-long-ids",
+ "id": "205",
+ "attributes": {
+ "content": "Added post content",
+ "created": "2015-03-11T04:31:00.0000000+00:00",
+ "title": "Added post"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/Post_with_empty_id_and_include_author_Response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/Post_with_empty_id_and_include_author_Response.json
new file mode 100644
index 00000000..f9996946
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/CreatingResources/Responses/Post_with_empty_id_and_include_author_Response.json
@@ -0,0 +1,65 @@
+{
+ "data": {
+ "type": "posts",
+ "id": "230",
+ "attributes": {
+ "content": "The server generated my ID",
+ "created": "2015-04-13T09:09:00.0000000+00:00",
+ "title": "New post"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/230/relationships/author",
+ "related": "https://www.example.com/posts/230/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "401"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/230/relationships/comments",
+ "related": "https://www.example.com/posts/230/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/230/relationships/tags",
+ "related": "https://www.example.com/posts/230/tags"
+ }
+ }
+ }
+ },
+ "included": [
+ {
+ "type": "users",
+ "id": "401",
+ "attributes": {
+ "first-name": "Alice",
+ "last-name": "Smith"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/comments",
+ "related": "https://www.example.com/users/401/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/posts",
+ "related": "https://www.example.com/users/401/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/user-groups",
+ "related": "https://www.example.com/users/401/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_included_to_one_response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_included_to_one_response.json
new file mode 100644
index 00000000..98fcd282
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_included_to_one_response.json
@@ -0,0 +1,65 @@
+{
+ "data": {
+ "type": "posts",
+ "id": "201",
+ "attributes": {
+ "content": "Post 1 content",
+ "created": "2015-01-31T14:00:00.0000000+00:00",
+ "title": "Post 1"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/author",
+ "related": "https://www.example.com/posts/201/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "401"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/comments",
+ "related": "https://www.example.com/posts/201/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/tags",
+ "related": "https://www.example.com/posts/201/tags"
+ }
+ }
+ }
+ },
+ "included": [
+ {
+ "type": "users",
+ "id": "401",
+ "attributes": {
+ "first-name": "Alice",
+ "last-name": "Smith"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/comments",
+ "related": "https://www.example.com/users/401/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/posts",
+ "related": "https://www.example.com/users/401/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/user-groups",
+ "related": "https://www.example.com/users/401/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_external_response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_external_response.json
new file mode 100644
index 00000000..e8008645
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_external_response.json
@@ -0,0 +1,164 @@
+{
+ "data": [
+ {
+ "type": "posts",
+ "id": "201",
+ "attributes": {
+ "content": "Post 1 content",
+ "created": "2015-01-31T14:00:00.0000000+00:00",
+ "title": "Post 1"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/author",
+ "related": "https://www.example.com/posts/201/author"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/comments",
+ "related": "https://www.example.com/posts/201/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/201/relationships/tags",
+ "related": "https://www.example.com/posts/201/tags"
+ },
+ "data": [
+ {
+ "type": "tags",
+ "id": "301"
+ },
+ {
+ "type": "tags",
+ "id": "302"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "posts",
+ "id": "202",
+ "attributes": {
+ "content": "Post 2 content",
+ "created": "2015-02-05T08:10:00.0000000+00:00",
+ "title": "Post 2"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/author",
+ "related": "https://www.example.com/posts/202/author"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/comments",
+ "related": "https://www.example.com/posts/202/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/tags",
+ "related": "https://www.example.com/posts/202/tags"
+ },
+ "data": [
+ {
+ "type": "tags",
+ "id": "302"
+ },
+ {
+ "type": "tags",
+ "id": "303"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "posts",
+ "id": "203",
+ "attributes": {
+ "content": "Post 3 content",
+ "created": "2015-02-07T11:11:00.0000000+00:00",
+ "title": "Post 3"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/author",
+ "related": "https://www.example.com/posts/203/author"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/comments",
+ "related": "https://www.example.com/posts/203/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/tags",
+ "related": "https://www.example.com/posts/203/tags"
+ },
+ "data": [
+ {
+ "type": "tags",
+ "id": "303"
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "included": [
+ {
+ "type": "tags",
+ "id": "301",
+ "attributes": {
+ "name": "Tag A"
+ },
+ "relationships": {
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/tags/301/relationships/posts",
+ "related": "https://www.example.com/tags/301/posts"
+ }
+ }
+ }
+ },
+ {
+ "type": "tags",
+ "id": "302",
+ "attributes": {
+ "name": "Tag B"
+ },
+ "relationships": {
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/tags/302/relationships/posts",
+ "related": "https://www.example.com/tags/302/posts"
+ }
+ }
+ }
+ },
+ {
+ "type": "tags",
+ "id": "303",
+ "attributes": {
+ "name": "Tag C"
+ },
+ "relationships": {
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/tags/303/relationships/posts",
+ "related": "https://www.example.com/tags/303/posts"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_response.json
new file mode 100644
index 00000000..e1a01091
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_include_response.json
@@ -0,0 +1,140 @@
+{
+ "data": [
+ {
+ "type": "comments",
+ "id": "101",
+ "attributes": {
+ "created": "2015-01-31T14:30:00.0000000+00:00",
+ "text": "Comment 1"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/comments/101/relationships/author",
+ "related": "https://www.example.com/comments/101/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "403"
+ }
+ },
+ "post": {
+ "links": {
+ "self": "https://www.example.com/comments/101/relationships/post",
+ "related": "https://www.example.com/comments/101/post"
+ }
+ }
+ }
+ },
+ {
+ "type": "comments",
+ "id": "102",
+ "attributes": {
+ "created": "2015-01-31T14:35:00.0000000+00:00",
+ "text": "Comment 2"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/comments/102/relationships/author",
+ "related": "https://www.example.com/comments/102/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "402"
+ }
+ },
+ "post": {
+ "links": {
+ "self": "https://www.example.com/comments/102/relationships/post",
+ "related": "https://www.example.com/comments/102/post"
+ }
+ }
+ }
+ },
+ {
+ "type": "comments",
+ "id": "103",
+ "attributes": {
+ "created": "2015-01-31T14:41:00.0000000+00:00",
+ "text": "Comment 3"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/comments/103/relationships/author",
+ "related": "https://www.example.com/comments/103/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "403"
+ }
+ },
+ "post": {
+ "links": {
+ "self": "https://www.example.com/comments/103/relationships/post",
+ "related": "https://www.example.com/comments/103/post"
+ }
+ }
+ }
+ }
+ ],
+ "included": [
+ {
+ "type": "users",
+ "id": "403",
+ "attributes": {
+ "first-name": "Charlie",
+ "last-name": "Michaels"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/403/relationships/comments",
+ "related": "https://www.example.com/users/403/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/403/relationships/posts",
+ "related": "https://www.example.com/users/403/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/403/relationships/user-groups",
+ "related": "https://www.example.com/users/403/user-groups"
+ }
+ }
+ }
+ },
+ {
+ "type": "users",
+ "id": "402",
+ "attributes": {
+ "first-name": "Bob",
+ "last-name": "Jones"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/402/relationships/comments",
+ "related": "https://www.example.com/users/402/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/402/relationships/posts",
+ "related": "https://www.example.com/users/402/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/402/relationships/user-groups",
+ "related": "https://www.example.com/users/402/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_integer_key_response.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_integer_key_response.json
new file mode 100644
index 00000000..f99bab69
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/FetchingResources/Get_related_to_many_integer_key_response.json
@@ -0,0 +1,34 @@
+{
+ "data": [
+ {
+ "type": "children",
+ "id": "7501",
+ "attributes": {
+ "child-description": "Child 2 Description"
+ },
+ "relationships": {
+ "master": {
+ "links": {
+ "self": "https://www.example.com/children/7501/relationships/master",
+ "related": "https://www.example.com/children/7501/master"
+ }
+ }
+ }
+ },
+ {
+ "type": "children",
+ "id": "7502",
+ "attributes": {
+ "child-description": "Child 3 Description"
+ },
+ "relationships": {
+ "master": {
+ "links": {
+ "self": "https://www.example.com/children/7502/relationships/master",
+ "related": "https://www.example.com/children/7502/master"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetAllResponsePaged-2-2.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetAllResponsePaged-2-2.json
new file mode 100644
index 00000000..7bf2f5ba
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetAllResponsePaged-2-2.json
@@ -0,0 +1,66 @@
+{
+ "meta": {
+ "total-pages": 2,
+ "total-count": 4
+ },
+ "data": [
+ {
+ "type": "posts",
+ "id": "203",
+ "attributes": {
+ "content": "Post 3 content",
+ "created": "2015-02-07T11:11:00.0000000+00:00",
+ "title": "Post 3"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/author",
+ "related": "https://www.example.com/posts/203/author"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/comments",
+ "related": "https://www.example.com/posts/203/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/203/relationships/tags",
+ "related": "https://www.example.com/posts/203/tags"
+ }
+ }
+ }
+ },
+ {
+ "type": "posts",
+ "id": "204",
+ "attributes": {
+ "content": "Post 4 content",
+ "created": "2015-02-08T06:59:00.0000000+00:00",
+ "title": "Post 4"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/204/relationships/author",
+ "related": "https://www.example.com/posts/204/author"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/204/relationships/comments",
+ "related": "https://www.example.com/posts/204/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/204/relationships/tags",
+ "related": "https://www.example.com/posts/204/tags"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted-desc.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted-desc.json
new file mode 100644
index 00000000..b65cea48
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted-desc.json
@@ -0,0 +1,64 @@
+{
+ "meta": {
+ "total-pages": 2,
+ "total-count": 3
+ },
+ "data": [
+ {
+ "type": "users",
+ "id": "410",
+ "attributes": {
+ "first-name": "Sally",
+ "last-name": "Burns"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/410/relationships/comments",
+ "related": "https://www.example.com/users/410/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/410/relationships/posts",
+ "related": "https://www.example.com/users/410/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/410/relationships/user-groups",
+ "related": "https://www.example.com/users/410/user-groups"
+ }
+ }
+ }
+ },
+ {
+ "type": "users",
+ "id": "406",
+ "attributes": {
+ "first-name": "Ed",
+ "last-name": "Burns"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/comments",
+ "related": "https://www.example.com/users/406/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/posts",
+ "related": "https://www.example.com/users/406/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/user-groups",
+ "related": "https://www.example.com/users/406/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted.json
new file mode 100644
index 00000000..ee562655
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-1-2-sorted.json
@@ -0,0 +1,64 @@
+{
+ "meta": {
+ "total-pages": 2,
+ "total-count": 3
+ },
+ "data": [
+ {
+ "type": "users",
+ "id": "409",
+ "attributes": {
+ "first-name": "Charlie",
+ "last-name": "Burns"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/comments",
+ "related": "https://www.example.com/users/409/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/posts",
+ "related": "https://www.example.com/users/409/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/user-groups",
+ "related": "https://www.example.com/users/409/user-groups"
+ }
+ }
+ }
+ },
+ {
+ "type": "users",
+ "id": "406",
+ "attributes": {
+ "first-name": "Ed",
+ "last-name": "Burns"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/comments",
+ "related": "https://www.example.com/users/406/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/posts",
+ "related": "https://www.example.com/users/406/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/406/relationships/user-groups",
+ "related": "https://www.example.com/users/406/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-2-1.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-2-1.json
new file mode 100644
index 00000000..16e92908
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/Pagination/GetFilterPaged-2-1.json
@@ -0,0 +1,36 @@
+{
+ "meta": {
+ "total-pages": 3,
+ "total-count": 3
+ },
+ "data": [
+ {
+ "type": "users",
+ "id": "409",
+ "attributes": {
+ "first-name": "Charlie",
+ "last-name": "Burns"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/comments",
+ "related": "https://www.example.com/users/409/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/posts",
+ "related": "https://www.example.com/users/409/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/409/relationships/user-groups",
+ "related": "https://www.example.com/users/409/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestID.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestID.json
new file mode 100644
index 00000000..ac7fbb4b
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestID.json
@@ -0,0 +1,9 @@
+{
+ "data": {
+ "type": "post-i-ds",
+ "id": "202",
+ "attributes": {
+ "title": "New post title"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestLongId.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestLongId.json
new file mode 100644
index 00000000..7358d7a8
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Requests/PatchWithAttributeUpdateRequestLongId.json
@@ -0,0 +1,9 @@
+{
+ "data": {
+ "type": "post-long-ids",
+ "id": "202",
+ "attributes": {
+ "title": "New post title"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseID.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseID.json
new file mode 100644
index 00000000..b0448cb5
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseID.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-i-ds",
+ "id": "202",
+ "attributes": {
+ "content": "Post 2 content",
+ "created": "2015-02-05T08:10:00.0000000+00:00",
+ "title": "New post title"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseLongId.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseLongId.json
new file mode 100644
index 00000000..fea5fa0a
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateResponseLongId.json
@@ -0,0 +1,11 @@
+{
+ "data": {
+ "type": "post-long-ids",
+ "id": "202",
+ "attributes": {
+ "content": "Post 2 content",
+ "created": "2015-02-05T08:10:00.0000000+00:00",
+ "title": "New post title"
+ }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateWithIncludeResponse.json b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateWithIncludeResponse.json
new file mode 100644
index 00000000..6bff40d9
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/Fixtures/UpdatingResources/Responses/PatchWithAttributeUpdateWithIncludeResponse.json
@@ -0,0 +1,65 @@
+{
+ "data": {
+ "type": "posts",
+ "id": "202",
+ "attributes": {
+ "content": "Post 2 content",
+ "created": "2015-02-05T08:10:00.0000000+00:00",
+ "title": "New post title"
+ },
+ "relationships": {
+ "author": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/author",
+ "related": "https://www.example.com/posts/202/author"
+ },
+ "data": {
+ "type": "users",
+ "id": "401"
+ }
+ },
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/comments",
+ "related": "https://www.example.com/posts/202/comments"
+ }
+ },
+ "tags": {
+ "links": {
+ "self": "https://www.example.com/posts/202/relationships/tags",
+ "related": "https://www.example.com/posts/202/tags"
+ }
+ }
+ }
+ },
+ "included": [
+ {
+ "type": "users",
+ "id": "401",
+ "attributes": {
+ "first-name": "Alice",
+ "last-name": "Smith"
+ },
+ "relationships": {
+ "comments": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/comments",
+ "related": "https://www.example.com/users/401/comments"
+ }
+ },
+ "posts": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/posts",
+ "related": "https://www.example.com/users/401/posts"
+ }
+ },
+ "user-groups": {
+ "links": {
+ "self": "https://www.example.com/users/401/relationships/user-groups",
+ "related": "https://www.example.com/users/401/user-groups"
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests.csproj b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests.csproj
index 7f2a1699..35d82430 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests.csproj
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests.csproj
@@ -61,6 +61,10 @@
..\packages\Microsoft.Owin.Testing.3.0.0\lib\net45\Microsoft.Owin.Testing.dll
+
+ ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
+ True
+
..\packages\NMemory.1.0.1\lib\net45\NMemory.dll
@@ -71,7 +75,15 @@
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll
+ True
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll
+ True
+
@@ -94,10 +106,12 @@
+
+
@@ -107,6 +121,10 @@
{76dee472-723b-4be6-8b97-428ac326e30f}
JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
+
+ {AF7861F3-550B-4F70-A33E-1E5F48D39333}
+ JSONAPI.Autofac
+
{52b19fd6-efaa-45b5-9c3e-a652e27608d1}
JSONAPI
@@ -123,9 +141,21 @@
+
+ Always
+
+
+ Always
+
Always
+
+ Always
+
+
+ Always
+
Always
@@ -233,8 +263,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/PaginationTests.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/PaginationTests.cs
new file mode 100644
index 00000000..0db20f57
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/PaginationTests.cs
@@ -0,0 +1,77 @@
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
+{
+ [TestClass]
+ public class PaginationTests : AcceptanceTestsBase
+ {
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetPage2Post2()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "posts?page[number]=1&page[size]=2");
+
+ await AssertResponseContent(response, @"Fixtures\Pagination\GetAllResponsePaged-2-2.json", HttpStatusCode.OK);
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetWithFilter()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "users?filter[last-name]=Burns&page[number]=1&page[size]=1");
+
+ await AssertResponseContent(response, @"Fixtures\Pagination\GetFilterPaged-2-1.json", HttpStatusCode.OK);
+ }
+ }
+
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetWithFilterSortedAscTest()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "users?filter[last-name]=Burns&page[number]=0&page[size]=2&sort=first-name");
+
+ await AssertResponseContent(response, @"Fixtures\Pagination\GetFilterPaged-1-2-sorted.json", HttpStatusCode.OK);
+ }
+ }
+
+
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task GetWithFilterSortedDescTest()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitGet(effortConnection, "users?filter[last-name]=Burns&page[number]=0&page[size]=2&sort=-first-name");
+
+ await AssertResponseContent(response, @"Fixtures\Pagination\GetFilterPaged-1-2-sorted-desc.json", HttpStatusCode.OK);
+ }
+ }
+
+ }
+}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/UpdatingResourcesTests.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/UpdatingResourcesTests.cs
index 9d898537..e24ae49b 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/UpdatingResourcesTests.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/UpdatingResourcesTests.cs
@@ -41,6 +41,81 @@ public async Task PatchWithAttributeUpdate()
}
}
+ [TestMethod]
+ [DeploymentItem(@"Data\Comment.csv", @"Data")]
+ [DeploymentItem(@"Data\Post.csv", @"Data")]
+ [DeploymentItem(@"Data\PostTagLink.csv", @"Data")]
+ [DeploymentItem(@"Data\Tag.csv", @"Data")]
+ [DeploymentItem(@"Data\User.csv", @"Data")]
+ public async Task PatchWithAttributeUpdateAndInclude()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPatch(effortConnection, "posts/202?include=author", @"Fixtures\UpdatingResources\Requests\PatchWithAttributeUpdateRequest.json");
+
+ await AssertResponseContent(response, @"Fixtures\UpdatingResources\Responses\PatchWithAttributeUpdateWithIncludeResponse.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.Posts.Include(p => p.Tags).ToArray();
+ allPosts.Length.Should().Be(4);
+ var actualPost = allPosts.First(t => t.Id == "202");
+ actualPost.Id.Should().Be("202");
+ actualPost.Title.Should().Be("New post title");
+ actualPost.Content.Should().Be("Post 2 content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 02, 05, 08, 10, 0, new TimeSpan(0)));
+ actualPost.AuthorId.Should().Be("401");
+ actualPost.Tags.Select(t => t.Id).Should().BeEquivalentTo("302", "303");
+ }
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostID.csv", @"Data")]
+ public async Task PatchWithAttributeUpdateID()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPatch(effortConnection, "post-i-ds/202", @"Fixtures\UpdatingResources\Requests\PatchWithAttributeUpdateRequestID.json");
+
+ await AssertResponseContent(response, @"Fixtures\UpdatingResources\Responses\PatchWithAttributeUpdateResponseID.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsID;
+ allPosts.Count().Should().Be(4);
+ var actualPost = allPosts.First(t => t.ID == "202");
+ actualPost.ID.Should().Be("202");
+ actualPost.Title.Should().Be("New post title");
+ actualPost.Content.Should().Be("Post 2 content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 02, 05, 08, 10, 0, new TimeSpan(0)));
+ }
+ }
+ }
+
+ [TestMethod]
+ [DeploymentItem(@"Data\PostLongId.csv", @"Data")]
+ public async Task PatchWithAttributeUpdateLongId()
+ {
+ using (var effortConnection = GetEffortConnection())
+ {
+ var response = await SubmitPatch(effortConnection, "post-long-ids/202", @"Fixtures\UpdatingResources\Requests\PatchWithAttributeUpdateRequestLongId.json");
+
+ await AssertResponseContent(response, @"Fixtures\UpdatingResources\Responses\PatchWithAttributeUpdateResponseLongId.json", HttpStatusCode.OK);
+
+ using (var dbContext = new TestDbContext(effortConnection, false))
+ {
+ var allPosts = dbContext.PostsLongId;
+ allPosts.Count().Should().Be(4);
+ var actualPost = allPosts.First(t => t.Id == 202);
+ actualPost.Id.Should().Be(202);
+ actualPost.Title.Should().Be("New post title");
+ actualPost.Content.Should().Be("Post 2 content");
+ actualPost.Created.Should().Be(new DateTimeOffset(2015, 02, 05, 08, 10, 0, new TimeSpan(0)));
+ }
+ }
+ }
+
[TestMethod]
[DeploymentItem(@"Data\Comment.csv", @"Data")]
[DeploymentItem(@"Data\Post.csv", @"Data")]
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/packages.config b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/packages.config
index f704445b..0243217e 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/packages.config
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/packages.config
@@ -3,9 +3,12 @@
+
+
+
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Controllers/SamplesController.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Controllers/SamplesController.cs
index 66588415..4d6d7546 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Controllers/SamplesController.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Controllers/SamplesController.cs
@@ -1,6 +1,8 @@
using System;
using System.Web.Http;
using JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Controllers
{
@@ -44,7 +46,10 @@ public IHttpActionResult GetSamples()
StringField = null,
EnumField = default(SampleEnum),
NullableEnumField = null,
- ComplexAttributeField = null
+ ComplexAttributeField = null,
+ JTokenStringField = null,
+ JTokenObjectField = null,
+ JTokenArrayField = null
};
var s2 = new Sample
{
@@ -82,10 +87,27 @@ public IHttpActionResult GetSamples()
StringField = "Some string 156",
EnumField = SampleEnum.Value1,
NullableEnumField = SampleEnum.Value2,
- ComplexAttributeField = "{\"foo\": { \"baz\": [11] }, \"bar\": 5}"
+ ComplexAttributeField = "{\"foo\": { \"baz\": [11] }, \"bar\": 5}",
+ JTokenStringField = "Andrew Jackson",
+ JTokenObjectField = JToken.FromObject(new SomeSerializableClass { MyField1 = "Abraham Lincoln", MyField2 = true, MyField3 = 439 }),
+ JTokenArrayField = new JArray(
+ JToken.FromObject(new SomeSerializableClass { MyField1 = "George Washington", MyField2 = null, MyField3 = 216 }),
+ JToken.FromObject(new SomeSerializableClass { MyField1 = "Thomas Jefferson", MyField2 = false, MyField3 = 631 }))
};
return Ok(new[] { s1, s2 });
}
+
+ [Serializable]
+ public class SomeSerializableClass
+ {
+ [JsonProperty("my-field1")]
+ public string MyField1 { get; set; }
+
+ [JsonProperty("overridden-field2")]
+ public bool? MyField2 { get; set; }
+
+ public int MyField3 { get; set; }
+ }
}
}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipDocumentMaterializer.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipDocumentMaterializer.cs
index 1f5ad325..9eb3fa47 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipDocumentMaterializer.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipDocumentMaterializer.cs
@@ -21,10 +21,11 @@ public StarshipDocumentMaterializer(
TestDbContext dbContext,
IQueryableResourceCollectionDocumentBuilder queryableResourceCollectionDocumentBuilder,
IBaseUrlService baseUrlService, ISingleResourceDocumentBuilder singleResourceDocumentBuilder,
+ ISortExpressionExtractor sortExpressionExtractor,
IQueryableEnumerationTransformer queryableEnumerationTransformer, IResourceTypeRegistry resourceTypeRegistry)
: base(
queryableResourceCollectionDocumentBuilder, baseUrlService, singleResourceDocumentBuilder,
- queryableEnumerationTransformer, resourceTypeRegistry)
+ queryableEnumerationTransformer, sortExpressionExtractor, resourceTypeRegistry)
{
_dbContext = dbContext;
}
@@ -61,7 +62,7 @@ public override Task UpdateRecord(string id, ISingleRes
throw new NotImplementedException();
}
- public override Task DeleteRecord(string id, CancellationToken cancellationToken)
+ public override Task DeleteRecord(string id, HttpRequestMessage request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipOfficersRelatedResourceMaterializer.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipOfficersRelatedResourceMaterializer.cs
index 750a1faa..9fb646b3 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipOfficersRelatedResourceMaterializer.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/DocumentMaterializers/StarshipOfficersRelatedResourceMaterializer.cs
@@ -6,6 +6,7 @@
using JSONAPI.Core;
using JSONAPI.Documents.Builders;
using JSONAPI.EntityFramework.Http;
+using JSONAPI.Http;
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.DocumentMaterializers
{
@@ -15,8 +16,10 @@ public class StarshipOfficersRelatedResourceMaterializer : EntityFrameworkToMany
public StarshipOfficersRelatedResourceMaterializer(ResourceTypeRelationship relationship, DbContext dbContext,
IQueryableResourceCollectionDocumentBuilder queryableResourceCollectionDocumentBuilder,
+ ISortExpressionExtractor sortExpressionExtractor,
+ IIncludeExpressionExtractor includeExpressionExtractor,
IResourceTypeRegistration primaryTypeRegistration)
- : base(relationship, dbContext, queryableResourceCollectionDocumentBuilder, primaryTypeRegistration)
+ : base(relationship, dbContext, queryableResourceCollectionDocumentBuilder, sortExpressionExtractor, includeExpressionExtractor, primaryTypeRegistration)
{
_dbContext = dbContext;
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.csproj b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.csproj
index 26e9df66..0a5148ea 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.csproj
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.csproj
@@ -1,212 +1,216 @@
-
-
-
-
- Debug
- AnyCPU
-
-
- 2.0
- {76DEE472-723B-4BE6-8B97-428AC326E30F}
- {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
- Library
- Properties
- JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
- JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
- v4.5
- true
-
-
-
-
- ..\
- true
-
-
- true
- full
- false
- bin\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\
- TRACE
- prompt
- 4
-
-
-
- False
- ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll
-
-
- False
- ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll
-
-
- False
- ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll
-
-
- False
- ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll
-
-
- False
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
-
-
- False
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
-
-
-
- ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll
-
-
- ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
-
-
- False
- ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\Owin.1.0\lib\net40\Owin.dll
-
-
-
- False
- ..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll
-
-
-
-
-
-
-
-
-
-
-
- False
- ..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll
-
-
- ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.2\lib\net45\System.Web.Http.Owin.dll
-
-
-
-
-
-
-
-
-
-
-
-
- Web.config
-
-
- Web.config
-
-
-
-
- Designer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {64abe648-efcb-46ee-9e1a-e163f52bf372}
- JSONAPI.Autofac.EntityFramework
-
-
- {af7861f3-550b-4f70-a33e-1e5f48d39333}
- JSONAPI.Autofac
-
-
- {e906356c-93f6-41f6-9a0d-73b8a99aa53c}
- JSONAPI.EntityFramework
-
-
- {52b19fd6-efaa-45b5-9c3e-a652e27608d1}
- JSONAPI
-
-
-
- 10.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
-
-
-
-
-
-
-
-
- True
- True
- 9283
- /
- http://localhost:9283/
- False
- False
-
-
- False
-
-
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {76DEE472-723B-4BE6-8B97-428AC326E30F}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
+ JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
+ v4.5
+ true
+
+
+
+
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll
+
+
+ False
+ ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll
+
+
+ False
+ ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll
+
+
+ False
+ ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll
+
+
+ False
+ ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
+
+
+ False
+ ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
+
+
+
+ ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll
+
+
+ ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
+
+
+ False
+ ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\Owin.1.0\lib\net40\Owin.dll
+
+
+
+ False
+ ..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll
+
+
+
+
+
+
+
+
+
+
+
+ False
+ ..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.2\lib\net45\System.Web.Http.Owin.dll
+
+
+
+
+
+
+
+
+
+
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {64abe648-efcb-46ee-9e1a-e163f52bf372}
+ JSONAPI.Autofac.EntityFramework
+
+
+ {af7861f3-550b-4f70-a33e-1e5f48d39333}
+ JSONAPI.Autofac
+
+
+ {e906356c-93f6-41f6-9a0d-73b8a99aa53c}
+ JSONAPI.EntityFramework
+
+
+ {52b19fd6-efaa-45b5-9c3e-a652e27608d1}
+ JSONAPI
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+ True
+ True
+ 9283
+ /
+ http://localhost:9283/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Child.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Child.cs
new file mode 100644
index 00000000..b3aefcc7
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Child.cs
@@ -0,0 +1,21 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Newtonsoft.Json;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models
+{
+ public class Child
+ {
+ public int Id { get; set; }
+
+ public string ChildDescription { get; set; }
+
+ [Required]
+ [JsonIgnore]
+ public int MasterId { get; set; }
+
+ [ForeignKey("MasterId")]
+ public Master Master { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Master.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Master.cs
new file mode 100644
index 00000000..5ad2015c
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Master.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models
+{
+ public class Master
+ {
+ public int Id { get; set; }
+
+ public string Description { get; set; }
+
+ public virtual ICollection Children { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostID.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostID.cs
new file mode 100644
index 00000000..4e7bd60f
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostID.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Newtonsoft.Json;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models
+{
+ public class PostID
+ {
+ [Key]
+ public string ID { get; set; }
+
+ public string Title { get; set; }
+
+ public string Content { get; set; }
+
+ public DateTimeOffset Created { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostLongId.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostLongId.cs
new file mode 100644
index 00000000..24e830d5
--- /dev/null
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/PostLongId.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Newtonsoft.Json;
+
+namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models
+{
+ public class PostLongId
+ {
+ [Key]
+ public long Id { get; set; }
+
+ public string Title { get; set; }
+
+ public string Content { get; set; }
+
+ public DateTimeOffset Created { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Sample.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Sample.cs
index c56ea671..e8096599 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Sample.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/Sample.cs
@@ -1,5 +1,6 @@
using System;
using JSONAPI.Attributes;
+using Newtonsoft.Json.Linq;
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models
{
@@ -48,5 +49,11 @@ public class Sample
[SerializeAsComplex]
public string ComplexAttributeField { get; set; }
+
+ public JToken JTokenStringField { get; set; }
+
+ public JToken JTokenObjectField { get; set; }
+
+ public JToken JTokenArrayField { get; set; }
}
}
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/TestDbContext.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/TestDbContext.cs
index 7efbb549..4850d0a0 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/TestDbContext.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Models/TestDbContext.cs
@@ -40,6 +40,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
public DbSet Comments { get; set; }
public DbSet Languages { get; set; }
public DbSet Posts { get; set; }
+ public DbSet PostsID { get; set; }
+ public DbSet PostsLongId { get; set; }
public DbSet Tags { get; set; }
public DbSet Users { get; set; }
public DbSet LanguageUserLinks { get; set; }
@@ -47,5 +49,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
public DbSet StarshipClasses { get; set; }
public DbSet Officers { get; set; }
public DbSet StarshipOfficerLinks { get; set; }
+ public DbSet Masters { get; set; }
+ public DbSet Children { get; set; }
}
}
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Properties/AssemblyInfo.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Properties/AssemblyInfo.cs
index 02097fc4..130abbb8 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Properties/AssemblyInfo.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Properties/AssemblyInfo.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -32,3 +33,4 @@
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: InternalsVisibleTo("JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests")]
\ No newline at end of file
diff --git a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Startup.cs b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Startup.cs
index ae14df36..678f8532 100644
--- a/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Startup.cs
+++ b/JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp/Startup.cs
@@ -13,6 +13,9 @@
using JSONAPI.EntityFramework;
using JSONAPI.EntityFramework.Configuration;
using Owin;
+using System.Collections.Generic;
+using JSONAPI.Documents.Builders;
+using JSONAPI.EntityFramework.Documents.Builders;
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp
{
@@ -33,7 +36,35 @@ public Startup(Func dbContextFactory)
public void Configuration(IAppBuilder app)
{
- var configuration = new JsonApiConfiguration();
+ /* these steps are divided in multiple methods to support better acceptance tests
+ * in production all the steps can be made inside the Configuration method.
+ */
+ var configuration = BuildConfiguration();
+
+ var configurator = BuildAutofacConfigurator(app);
+
+ var httpConfig = BuildHttpConfiguration();
+
+ MergeAndSetupConfiguration(app, configurator, httpConfig, configuration);
+ }
+
+ internal void MergeAndSetupConfiguration(IAppBuilder app, JsonApiHttpAutofacConfigurator configurator,
+ HttpConfiguration httpConfig, JsonApiConfiguration configuration)
+ {
+ configurator.Apply(httpConfig, configuration);
+ app.UseWebApi(httpConfig);
+ app.UseAutofacWebApi(httpConfig);
+ }
+
+ ///
+ /// Build up the which registers all the model types and their mappings.
+ ///
+ ///
+ internal JsonApiConfiguration BuildConfiguration()
+ {
+ var configuration = new JsonApiConfiguration(
+ new Core.PluralizationService(
+ new Dictionary { { "Child", "Children" } }));
configuration.RegisterEntityFrameworkResourceType();
configuration.RegisterEntityFrameworkResourceType();
configuration.RegisterEntityFrameworkResourceType();
@@ -45,6 +76,8 @@ public void Configuration(IAppBuilder app)
c.OverrideDefaultSortById(LanguageUserLinkSortByIdFactory);
});
configuration.RegisterResourceType();
+ configuration.RegisterResourceType();
+ configuration.RegisterResourceType();
configuration.RegisterEntityFrameworkResourceType();
configuration.RegisterEntityFrameworkResourceType();
configuration.RegisterEntityFrameworkResourceType();
@@ -58,7 +91,18 @@ public void Configuration(IAppBuilder app)
rc => rc.UseMaterializer());
}); // Example of a resource that is mapped from a DB entity
configuration.RegisterResourceType();
+ configuration.RegisterEntityFrameworkResourceType();
+ configuration.RegisterEntityFrameworkResourceType();
+ return configuration;
+ }
+ ///
+ /// Build up the which registers , modules and materializer
+ ///
+ ///
+ ///
+ internal JsonApiHttpAutofacConfigurator BuildAutofacConfigurator(IAppBuilder app)
+ {
var configurator = new JsonApiHttpAutofacConfigurator();
configurator.OnApplicationLifetimeScopeCreating(builder =>
{
@@ -70,13 +114,23 @@ public void Configuration(IAppBuilder app)
builder.RegisterType()
.As();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
+ builder.RegisterType().As();
+
});
configurator.OnApplicationLifetimeScopeBegun(applicationLifetimeScope =>
{
// TODO: is this a candidate for spinning into a JSONAPI.Autofac.WebApi.Owin package? Yuck
app.UseAutofacMiddleware(applicationLifetimeScope);
});
+ return configurator;
+ }
+ ///
+ /// Build up the with additional routes
+ ///
+ ///
+ internal HttpConfiguration BuildHttpConfiguration()
+ {
var httpConfig = new HttpConfiguration
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
@@ -86,12 +140,10 @@ public void Configuration(IAppBuilder app)
httpConfig.Routes.MapHttpRoute("Samples", "samples", new { Controller = "Samples" });
httpConfig.Routes.MapHttpRoute("Search", "search", new { Controller = "Search" });
httpConfig.Routes.MapHttpRoute("Trees", "trees", new { Controller = "Trees" });
-
- configurator.Apply(httpConfig, configuration);
- app.UseWebApi(httpConfig);
- app.UseAutofacWebApi(httpConfig);
+ return httpConfig;
}
+
private BinaryExpression LanguageUserLinkFilterByIdFactory(ParameterExpression param, string id)
{
var split = id.Split('_');
diff --git a/JSONAPI.Autofac/JsonApiAutofacModule.cs b/JSONAPI.Autofac/JsonApiAutofacModule.cs
index 69881ce1..dd248aa8 100644
--- a/JSONAPI.Autofac/JsonApiAutofacModule.cs
+++ b/JSONAPI.Autofac/JsonApiAutofacModule.cs
@@ -16,7 +16,7 @@ public class JsonApiAutofacModule : Module
{
private readonly IJsonApiConfiguration _jsonApiConfiguration;
- internal JsonApiAutofacModule(IJsonApiConfiguration jsonApiConfiguration)
+ public JsonApiAutofacModule(IJsonApiConfiguration jsonApiConfiguration)
{
_jsonApiConfiguration = jsonApiConfiguration;
}
@@ -126,7 +126,14 @@ protected override void Load(ContainerBuilder builder)
});
builder.RegisterType().SingleInstance();
- builder.RegisterType().As().SingleInstance();
+ if (_jsonApiConfiguration.CustomBaseUrlService != null)
+ {
+ builder.Register(c => _jsonApiConfiguration.CustomBaseUrlService).As().SingleInstance();
+ }
+ else
+ {
+ builder.RegisterType().As().SingleInstance();
+ }
builder.RegisterType().As().InstancePerRequest();
// Serialization
@@ -157,6 +164,9 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType().SingleInstance();
builder.RegisterType().As();
+ // Misc
+ builder.RegisterType().As().SingleInstance();
+ builder.RegisterType().As().SingleInstance();
}
}
}
diff --git a/JSONAPI.Autofac/JsonApiHttpAutofacConfigurator.cs b/JSONAPI.Autofac/JsonApiHttpAutofacConfigurator.cs
index 2fa79374..035f8c09 100644
--- a/JSONAPI.Autofac/JsonApiHttpAutofacConfigurator.cs
+++ b/JSONAPI.Autofac/JsonApiHttpAutofacConfigurator.cs
@@ -52,7 +52,7 @@ public void Apply(HttpConfiguration httpConfiguration, IJsonApiConfiguration jso
_appLifetimeScopeBegunAction(applicationLifetimeScope);
var jsonApiHttpConfiguration = applicationLifetimeScope.Resolve();
- jsonApiHttpConfiguration.Apply(httpConfiguration);
+ jsonApiHttpConfiguration.Apply(httpConfiguration, jsonApiConfiguration);
httpConfiguration.DependencyResolver = new AutofacWebApiDependencyResolver(applicationLifetimeScope);
}
diff --git a/JSONAPI.EntityFramework.Tests/DbContextExtensionsTests.cs b/JSONAPI.EntityFramework.Tests/DbContextExtensionsTests.cs
index 4b8d4ed2..8548b6bd 100644
--- a/JSONAPI.EntityFramework.Tests/DbContextExtensionsTests.cs
+++ b/JSONAPI.EntityFramework.Tests/DbContextExtensionsTests.cs
@@ -17,6 +17,7 @@ private class TestDbContext : DbContext
{
public DbSet Backlinks { get; set; }
public DbSet Posts { get; set; }
+ public DbSet PostIDs { get; set; }
public TestDbContext(DbConnection conn) : base(conn, true)
{
@@ -34,6 +35,10 @@ private class SubPost : Post
{
public string Foo { get; set; }
}
+ private class SubPostID : PostID
+ {
+ public string Foo { get; set; }
+ }
private DbConnection _conn;
private TestDbContext _context;
@@ -71,6 +76,17 @@ public void GetKeyNamesStandardIdTest()
keyNames.First().Should().Be("Id");
}
+ [TestMethod]
+ public void GetKeyNamesStandardIDTest()
+ {
+ // Act
+ IEnumerable keyNames = _context.GetKeyNames(typeof(PostID)).ToArray();
+
+ // Assert
+ keyNames.Count().Should().Be(1);
+ keyNames.First().Should().Be("ID");
+ }
+
[TestMethod]
public void GetKeyNamesNonStandardIdTest()
{
@@ -93,6 +109,17 @@ public void GetKeyNamesForChildClass()
keyNames.First().Should().Be("Id");
}
+ [TestMethod]
+ public void GetKeyNamesForChildClassID()
+ {
+ // Act
+ IEnumerable keyNames = _context.GetKeyNames(typeof(SubPostID)).ToArray();
+
+ // Assert
+ keyNames.Count().Should().Be(1);
+ keyNames.First().Should().Be("ID");
+ }
+
[TestMethod]
public void GetKeyNamesNotAnEntityTest()
{
diff --git a/JSONAPI.EntityFramework.Tests/JSONAPI.EntityFramework.Tests.csproj b/JSONAPI.EntityFramework.Tests/JSONAPI.EntityFramework.Tests.csproj
index 2938cf78..6e341345 100644
--- a/JSONAPI.EntityFramework.Tests/JSONAPI.EntityFramework.Tests.csproj
+++ b/JSONAPI.EntityFramework.Tests/JSONAPI.EntityFramework.Tests.csproj
@@ -119,6 +119,7 @@
+
diff --git a/JSONAPI.EntityFramework.Tests/Models/PostID.cs b/JSONAPI.EntityFramework.Tests/Models/PostID.cs
new file mode 100644
index 00000000..420e047a
--- /dev/null
+++ b/JSONAPI.EntityFramework.Tests/Models/PostID.cs
@@ -0,0 +1,21 @@
+namespace JSONAPI.EntityFramework.Tests.Models
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations.Schema;
+
+ public partial class PostID
+ {
+ public PostID()
+ {
+ this.Comments = new HashSet();
+ }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid ID { get; set; }
+ public string Title { get; set; }
+
+ public virtual Author Author { get; set; }
+ public virtual ICollection Comments { get; set; }
+ }
+}
diff --git a/JSONAPI.EntityFramework/Documents/Builders/EntityFrameworkQueryableResourceCollectionDocumentBuilder.cs b/JSONAPI.EntityFramework/Documents/Builders/EntityFrameworkQueryableResourceCollectionDocumentBuilder.cs
new file mode 100644
index 00000000..1bd9e2c8
--- /dev/null
+++ b/JSONAPI.EntityFramework/Documents/Builders/EntityFrameworkQueryableResourceCollectionDocumentBuilder.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Data.Entity;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using JSONAPI.Documents;
+using JSONAPI.Documents.Builders;
+using JSONAPI.Http;
+using JSONAPI.QueryableTransformers;
+
+namespace JSONAPI.EntityFramework.Documents.Builders
+{
+ ///
+ /// Provides a entity framework implementation of an IQueryableResourceCollectionDocumentBuilder
+ ///
+ public class EntityFrameworkQueryableResourceCollectionDocumentBuilder: DefaultQueryableResourceCollectionDocumentBuilder
+ {
+ ///
+ /// Creates a new EntityFrameworkQueryableResourceCollectionDocumentBuilder
+ ///
+ public EntityFrameworkQueryableResourceCollectionDocumentBuilder(
+ IResourceCollectionDocumentBuilder resourceCollectionDocumentBuilder,
+ IQueryableEnumerationTransformer enumerationTransformer,
+ IQueryableFilteringTransformer filteringTransformer,
+ IQueryableSortingTransformer sortingTransformer,
+ IQueryablePaginationTransformer paginationTransformer,
+ IBaseUrlService baseUrlService) :
+ base(resourceCollectionDocumentBuilder,
+ enumerationTransformer,
+ filteringTransformer,
+ sortingTransformer,
+ paginationTransformer,
+ baseUrlService)
+ {
+ }
+
+ ///
+ /// Returns the metadata that should be sent with this document.
+ ///
+ protected override async Task GetDocumentMetadata(IQueryable originalQuery, IQueryable filteredQuery, IOrderedQueryable sortedQuery,
+ IPaginationTransformResult paginationResult, CancellationToken cancellationToken)
+ {
+ var metadata = new Metadata();
+ if (paginationResult.PaginationWasApplied)
+ {
+ var count = await filteredQuery.CountAsync(cancellationToken);
+ metadata.MetaObject.Add("total-pages", (int)Math.Ceiling((decimal) count / paginationResult.PageSize));
+ metadata.MetaObject.Add("total-count", count);
+ }
+ if (metadata.MetaObject.HasValues)
+ return metadata;
+ return null;
+ }
+ }
+}
diff --git a/JSONAPI.EntityFramework/EntityFrameworkResourceObjectMaterializer.cs b/JSONAPI.EntityFramework/EntityFrameworkResourceObjectMaterializer.cs
index 57c2b29b..b0b26ea9 100644
--- a/JSONAPI.EntityFramework/EntityFrameworkResourceObjectMaterializer.cs
+++ b/JSONAPI.EntityFramework/EntityFrameworkResourceObjectMaterializer.cs
@@ -69,8 +69,10 @@ public async Task