Skip to content

[WIP] The big rewrite #96

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 66 commits into from
Aug 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
490aad8
rewrite JSONAPI.NET to be compatible with JSON API 1.0
Jun 28, 2015
6e5fcd5
update TodoMVC project to use autofac integration
Jun 28, 2015
3664d39
change autofac configuration interface
Jun 28, 2015
3088b6b
remove redundant resolver statement
Jun 28, 2015
fefc3f2
allow UseJsonApiWithAutofac to take ILifetimeScope
Jun 28, 2015
00dee31
move generic parameter to interface for IPayloadMaterializer
Jun 29, 2015
a243266
fix mis-named class
Jun 29, 2015
84472a7
remove IMaterializer, IMetadataManager, and implementations
Jun 29, 2015
b75ae4e
make EFPayloadMaterializer method virtual
Jun 29, 2015
32779c5
use current request to get base url
Jun 29, 2015
78e7718
Add metadata param to resource collection payload builder
Jun 29, 2015
dea14d0
implement related resource URLs
Jun 29, 2015
dafa96d
implement related resource URLs for to-one relationships
Jun 29, 2015
25345f8
make new related methods protected
Jun 29, 2015
fc7732d
add IBaseUrlService
Jun 29, 2015
455af08
add new convenience methods for making JsonApiException
Jun 29, 2015
597513c
support custom filter and sort expressions for registered types
Jun 29, 2015
41d2b95
remove sorting by +
Jun 29, 2015
ec1cda6
rearrange acceptance tests
Jun 30, 2015
1abdddc
don't fail on unknown fields; ignore instead
Jun 30, 2015
f13058b
include relationships when materializing
Jun 30, 2015
e34d6f9
separate out value converters into files
Jun 30, 2015
2963d60
rename primitives and general cleanup
Jul 1, 2015
e27868b
make sure TodoMVC sample works.
Jul 1, 2015
f3ddb88
Move namespace of queryable transformers
Jul 1, 2015
edcb959
ensure not found works for fetching resource by ID
Jul 1, 2015
dae593f
return 404 for to-one related resources to non-existant resource
Jul 1, 2015
e1a78d2
return 404 for to-many related actions where primary resource doesn't…
Jul 1, 2015
b4fb7d2
provide serialization hooks for RelationshipObjectFormatter children
Jul 2, 2015
bc083df
don't crash when requesting related resource for non-existent relatio…
Jul 2, 2015
2e54d6a
make GetBaseUrl method virtual
Jul 4, 2015
b2a5213
allow specifying includes and metadata for queryable builder
Jul 4, 2015
7dc407e
don't fail on null to-many relationship
Jul 7, 2015
ba3388b
remove extraneous select
Jul 7, 2015
d1256af
simplify async queryable enumeration transformer
Jul 9, 2015
4d0de76
add queryable enumeration transform method
Jul 9, 2015
bf14fe0
add materializer for mapping entities to DTOs
Jul 9, 2015
464d648
allow serializing null related resources
Jul 10, 2015
48e19a4
allow serializing metadata with ISingleResourceDocumentBuilder
Jul 10, 2015
5dfaf4b
add some documentation
Jul 10, 2015
13b8c32
add single record filter to IDocumentMaterializer
Jul 10, 2015
91ca6a3
rearrange acceptance test projects
Jul 11, 2015
e5f9eb2
Refactor configuration and document materialization systems
Jul 12, 2015
0d2091b
split into new files
Jul 12, 2015
68dfca7
remove unused delegates
Jul 13, 2015
c2eaa23
sanitize configuration interfaces slightly
Jul 13, 2015
6ca6010
add way to configure a default related resource materializer per reso…
Jul 13, 2015
b963cd6
more configuration interface cleanup
Jul 13, 2015
d10b334
fix a couple mis-named things
Jul 13, 2015
d973bb1
provide hook for configuring lifetime scope before it is built
Jul 13, 2015
9db8a94
allow customizing DefaultNamingConventions
Jul 13, 2015
6b0ccc1
add convenience method for constructing 403 errors
Jul 14, 2015
e14592e
have MappedDocumentMaterializer punt on created, update, and delete
Jul 15, 2015
e851748
store relationship configurations by name instead of property
Jul 15, 2015
6bf83cc
make GetKeyNames work recursively
Jul 16, 2015
015b7ff
add test for subclass
Jul 17, 2015
9e185df
Adding globalization for decimal, double and single for conversion.
cybermats Jul 17, 2015
74fd025
convert linkage objects to using IResourceIdentifiers
Jul 17, 2015
f0324f5
Merge pull request #97 from cybermats/payload2
csantero Jul 18, 2015
fbc430a
make sure null to-one included relationships are serialized with null…
Jul 19, 2015
96b57f8
await tasks in JsonApiFormatter
Jul 21, 2015
8321989
allow specifying include paths
Jul 22, 2015
05cb963
expose PathVisitor
Jul 27, 2015
2d442ce
make GET methods virtual
Jul 30, 2015
adea1cb
send correct query to GetDocumentMetadata
Aug 2, 2015
aa795f0
make SetValue work with JToken null
Aug 13, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1,046 changes: 1,046 additions & 0 deletions .vs/config/applicationhost.config

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,46 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FluentAssertions;
using JSONAPI.EntityFramework.Tests.TestWebApp;
using JSONAPI.EntityFramework.Tests.TestWebApp.Models;
using JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models;
using JSONAPI.Json;
using Microsoft.Owin.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JSONAPI.EntityFramework.Tests.Acceptance
namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
[TestClass]
public abstract class AcceptanceTestsBase
{
private const string JsonApiContentType = "application/vnd.api+json";
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("http://localhost");
private static readonly Uri BaseUri = new Uri("https://www.example.com");

protected static DbConnection GetEffortConnection()
{
return TestHelpers.GetEffortConnection(@"Acceptance\Data");
return TestHelpers.GetEffortConnection(@"Data");
}

protected static async Task AssertResponseContent(HttpResponseMessage response, string expectedResponseTextResourcePath, HttpStatusCode expectedStatusCode)
protected static 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 redactedResponse = GuidRegex.Replace(responseContent, "{{SOME_GUID}}");
redactedResponse = StackTraceRegex.Replace(redactedResponse, "\"stackTrace\":\"{{STACK_TRACE}}\"");
string actualResponse;
if (redactErrorData)
{
var redactedResponse = GuidRegex.Replace(responseContent, "{{SOME_GUID}}");
actualResponse = StackTraceRegex.Replace(redactedResponse, "\"stackTrace\":\"{{STACK_TRACE}}\"");
}
else
{
actualResponse = responseContent;
}

redactedResponse.Should().Be(expectedResponse);
response.Content.Headers.ContentType.MediaType.Should().Be("application/vnd.api+json");
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);
Expand All @@ -49,12 +57,12 @@ protected async Task<HttpResponseMessage> SubmitGet(DbConnection effortConnectio
{
using (var server = TestServer.Create(app =>
{
var startup = new Startup(context => new TestDbContext(effortConnection, false));
var startup = new Startup(() => new TestDbContext(effortConnection, false));
startup.Configuration(app);
}))
{
var uri = new Uri(BaseUri, requestPath);
var response = await server.CreateRequest(uri.ToString()).GetAsync();
var response = await server.CreateRequest(uri.ToString()).AddHeader("Accept", JsonApiContentType).GetAsync();
return response;
}
}
Expand All @@ -66,14 +74,15 @@ protected async Task<HttpResponseMessage> SubmitPost(DbConnection effortConnecti
{
using (var server = TestServer.Create(app =>
{
var startup = new Startup(context => new TestDbContext(effortConnection, false));
var startup = new Startup(() => new TestDbContext(effortConnection, false));
startup.Configuration(app);
}))
{
var uri = new Uri(BaseUri, requestPath);
var requestContent = TestHelpers.ReadEmbeddedFile(requestDataTextResourcePath);
var response = await server
.CreateRequest(uri.ToString())
.AddHeader("Accept", JsonApiContentType)
.And(request =>
{
request.Content = new StringContent(requestContent, Encoding.UTF8, "application/vnd.api+json");
Expand All @@ -90,14 +99,15 @@ protected async Task<HttpResponseMessage> SubmitPatch(DbConnection effortConnect
{
using (var server = TestServer.Create(app =>
{
var startup = new Startup(context => new TestDbContext(effortConnection, false));
var startup = new Startup(() => new TestDbContext(effortConnection, false));
startup.Configuration(app);
}))
{
var uri = new Uri(BaseUri, requestPath);
var requestContent = TestHelpers.ReadEmbeddedFile(requestDataTextResourcePath);
var response = await server
.CreateRequest(uri.ToString())
.AddHeader("Accept", JsonApiContentType)
.And(request =>
{
request.Content = new StringContent(requestContent, Encoding.UTF8, "application/vnd.api+json");
Expand All @@ -113,13 +123,14 @@ protected async Task<HttpResponseMessage> SubmitDelete(DbConnection effortConnec
{
using (var server = TestServer.Create(app =>
{
var startup = new Startup(context => new TestDbContext(effortConnection, false));
var startup = new Startup(() => new TestDbContext(effortConnection, false));
startup.Configuration(app);
}))
{
var uri = new Uri(BaseUri, requestPath);
var response = await server
.CreateRequest(uri.ToString())
.AddHeader("Accept", JsonApiContentType)
.SendAsync("DELETE");
return response;
}
Expand Down
37 changes: 37 additions & 0 deletions JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.2.0" newVersion="5.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.2.0" newVersion="5.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.2.0" newVersion="5.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
[TestClass]
public class AttributeSerializationTests : AcceptanceTestsBase
{
[TestMethod]
public async Task Attributes_of_various_types_serialize_correctly()
{
using (var effortConnection = GetEffortConnection())
{
var response = await SubmitGet(effortConnection, "samples");

await AssertResponseContent(response, @"Fixtures\AttributeSerialization\Attributes_of_various_types_serialize_correctly.json", HttpStatusCode.OK);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
[TestClass]
public class ComputedIdTests : AcceptanceTestsBase
{
[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_all_of_resource_with_computed_id()
{
using (var effortConnection = GetEffortConnection())
{
var response = await SubmitGet(effortConnection, "language-user-links");

await AssertResponseContent(response, @"Fixtures\ComputedId\Responses\Get_all_of_resource_with_computed_id_Response.json", HttpStatusCode.OK);
}
}

[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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using FluentAssertions;
using JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
[TestClass]
public class CreatingResourcesTests : 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 Post_with_client_provided_id()
{
using (var effortConnection = GetEffortConnection())
{
var response = await SubmitPost(effortConnection, "posts", @"Fixtures\CreatingResources\Requests\Post_with_client_provided_id_Request.json");

await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\Post_with_client_provided_id_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 == "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)));
actualPost.AuthorId.Should().Be("401");
}
}
}

[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()
{
using (var effortConnection = GetEffortConnection())
{
var response = await SubmitPost(effortConnection, "posts", @"Fixtures\CreatingResources\Requests\Post_with_empty_id_Request.json");

await AssertResponseContent(response, @"Fixtures\CreatingResources\Responses\Post_with_empty_id_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");
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Id,Address,OwnerCompanyId
"1000","123 Sesame St.","1100"
"1001","1600 Pennsylvania Avenue",
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Id,Name
"1100","Big Bird and Friends"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Id,Name
"9000","English"
"9001","French"
"9002","Spanish"
"9003","German"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
LanguageId,UserId,FluencyLevel
"9000","401","Native"
"9001","401","Conversational"
"9002","401","Fluent"
"9001","402","Native"
"9002","403","Native"
"9003","404","Native"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
OfficerId,Name,Rank
"12000","James T. Kirk","Captain"
"12010","Jean-Luc Picard","Captain"
"12011","William T. Riker","Commander"
"12012","Data","Lt. Commander"
"12013","Deanna Troi","Lt. Commander"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
StarshipId,Name,StarshipClassId
"NCC-1701","USS Enterprise","80001"
"NCC-1701-D","USS Enterprise","80002"
"NCC-74656","USS Voyager","80003"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
StarshipClassId,Name
"80001","Constitution"
"80002","Galaxy"
"80003","Intrepid"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
StarshipId,OfficerId,Position
"NCC-1701","12000","Commanding Officer"
"NCC-1701-D","12010","Commanding Officer"
"NCC-1701-D","12011","First Officer"
"NCC-1701-D","12012","Second Officer"
"NCC-1701-D","12013","Ship's Counselor"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using FluentAssertions;
using JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JSONAPI.AcceptanceTests.EntityFrameworkTestWebApp.Tests
{
[TestClass]
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")]
public async Task Delete()
{
using (var effortConnection = GetEffortConnection())
{
var response = await SubmitDelete(effortConnection, "posts/203");

var responseContent = await response.Content.ReadAsStringAsync();
responseContent.Should().Be("");
response.StatusCode.Should().Be(HttpStatusCode.NoContent);

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();
}
}
}
}
}
Loading