Skip to content

Commit 343912e

Browse files
authored
Feat/serializer context decoupling (#558)
* feat: started work on decoupling * feat: add total record count * feat: green tests, new managers * feat: changed startup * feat: most annoying commit of my life, rewriting dozens of controllers * feat: rename: QueryManager -> RequestManager, deeper seperation of concerns, 12 tests running... * feat: removed JsonApiContext dependency from controller, fixed namespaced tests * feat: decoupled controllers * chore: renamed resourcegraphbuilder, took out some extensions, made benchmarks work again * feat: json api context decoupling mroe and more * chore: readded solutions * feat: upgrading to 2.2, setting contextentity in middleware * fix: removed outdated authorization test * fix: requestmeta tests * fix: some acceptance tests * fix: pagination * fix: total records in meta * feat: introduced JsonApiActionFilter * fix: more tests * feat: decoupled deserializer and serializer * chore: remove / cleanup old serializers * chore: remove operation services * tests: unit tests client/server (de)serializers * chore: various edits to run tests * fix: rm dasherized resolver * chore: remove JsonApiContext and corresponding interface * chore: reorganize namespaces * chore: rm old tests * chore: rm unused document builder * chore: add comments to deserialization classes * chore: add comments * chore: rm IJsonApiContext from sort attr instantiation * feat: (re)introduced omit null value behaviour (and now omit default value behaviour too) using a serializer options provider * chore: remove more jsonapicontext references * chore: minor renames, add wiki for serialization * chore: improve wiki * chore: wiki * chore: wiki * chore: add a bunch of comments, renamed a few services * chore: wired up nested sparse field selection * chore: wired up omit behaviour * chore: prettied some comments * chore: remove (almost) all references to jsonapicontext in unit tests project * chore: removed last bits of jsonapicontext * fix: tests serialization passing again * chore: rename included query service to include query service * chore: improve comment * chore: improve comment * chore: delete jsonapicontext tests * chore: remove space * test: email config * chore: rename client / server (de)serializer to request/response (de)serializer * chore: rename client / server (de)serializer to request/response (de)serializer * feat: introduce IQueryParameter * chore: move request/response (de)serializer to client/server namespace for easier future isolation * chore: update namespaces * chore: rm textfile * chore: fixing failing unit tests * chore: rename query services, adjustment namespace * chore: reorganised test files for serialization, added relationship path serialization tests * feat: request relationship in response serializer * chore: wired up response serializer in formatter layer * feat: IGetRelationshipService now returns TResource instead of object, which decouples it from serializers requirements * fix: support for serving document of resource type different from request resource, as required by resourceservice.getrelationships() * chore: simplified linkbuilders * chore: several fixes for e2e tests * fix: various e2e tests, decoupled service layer from serialization format * fix: inclusion edgecase * chore: fix error formatting tests * chore: naming consistency sparsefield and include query services * chore: various adjustments to make e2e test project build and pass again * chore: fix build various e2e tests * chore: fix build various e2e tests * fix: e2e test Can_Include_Nested_Relationships * fix: e2e test Can_Patch_Entity * fix: e2e test Patch_Entity_With_HasMany_Does_Not_Include_Relationships * fix: e2e test Can_Create_Entity_With_Client_Defined_Id_If_Configured * chore: rename base document parser and builder * fix: e2e test Can_Create_Guid_Identifiable_Entity_With_Client_Defined_Id_If_Configured * fix: unit tests various * feat: reorganisation inheritance serialization layer * fix: unit tests after inheritance update * fix: wire up correct resource object builder implementation in serializers * fix: e2e remaining CreatingDataTests * chore: refactor creatingdata tests * fix: e2e test paging * fix: e2e controller tests * chore: wiring up new resource object builders to dependency graph * chore: response resource object builder unit test, restored repo and resourcedef unit tests * chore: finishing touches comments serialization * Feat/serialization wiki (#561) * docs: add decoupling architecture, wiki folder * docs: add v4 wiki * chore: restructure of intro * chore: v4
1 parent 446cba4 commit 343912e

File tree

274 files changed

+7723
-6005
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

274 files changed

+7723
-6005
lines changed

benchmarks/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Benchmarks {
99
class Program {
1010
static void Main(string[] args) {
1111
var switcher = new BenchmarkSwitcher(new[] {
12-
typeof(JsonApiDeserializer_Benchmarks),
12+
typeof(JsonApideserializer_Benchmarks),
1313
//typeof(JsonApiSerializer_Benchmarks),
1414
typeof(QueryParser_Benchmarks),
1515
typeof(LinkBuilder_GetNamespaceFromPath_Benchmarks),

benchmarks/Query/QueryParser_Benchmarks.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public class QueryParser_Benchmarks {
2222
private const string DESCENDING_SORT = "-" + ATTRIBUTE;
2323

2424
public QueryParser_Benchmarks() {
25-
var requestMock = new Mock<IRequestManager>();
26-
requestMock.Setup(m => m.GetContextEntity()).Returns(new ContextEntity {
25+
var requestMock = new Mock<IRequestContext>();
26+
requestMock.Setup(m => m.GetRequestResource()).Returns(new ContextEntity {
2727
Attributes = new List<AttrAttribute> {
2828
new AttrAttribute(ATTRIBUTE, ATTRIBUTE)
2929
}
@@ -59,8 +59,8 @@ private void Run(int iterations, Action action) {
5959
// this facade allows us to expose and micro-benchmark protected methods
6060
private class BenchmarkFacade : QueryParser {
6161
public BenchmarkFacade(
62-
IRequestManager requestManager,
63-
JsonApiOptions options) : base(requestManager, options) { }
62+
IRequestContext currentRequest,
63+
JsonApiOptions options) : base(currentRequest, options) { }
6464

6565
public void _ParseSortParameters(string value) => base.ParseSortParameters(value);
6666
}

benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using JsonApiDotNetCore.Managers.Contracts;
88
using JsonApiDotNetCore.Models;
99
using JsonApiDotNetCore.Serialization;
10+
using JsonApiDotNetCore.Serialization.Contracts;
11+
1012
using JsonApiDotNetCore.Services;
1113
using Moq;
1214
using Newtonsoft.Json;
@@ -15,7 +17,7 @@
1517
namespace Benchmarks.Serialization
1618
{
1719
[MarkdownExporter]
18-
public class JsonApiDeserializer_Benchmarks {
20+
public class JsonApideserializer_Benchmarks {
1921
private const string TYPE_NAME = "simple-types";
2022
private static readonly string Content = JsonConvert.SerializeObject(new Document {
2123
Data = new ResourceObject {
@@ -30,15 +32,15 @@ public class JsonApiDeserializer_Benchmarks {
3032
}
3133
});
3234

33-
private readonly JsonApiDeSerializer _jsonApiDeSerializer;
35+
private readonly JsonApideserializer _jsonApideserializer;
3436

35-
public JsonApiDeserializer_Benchmarks() {
37+
public JsonApideserializer_Benchmarks() {
3638
var resourceGraphBuilder = new ResourceGraphBuilder();
3739
resourceGraphBuilder.AddResource<SimpleType>(TYPE_NAME);
3840
var resourceGraph = resourceGraphBuilder.Build();
39-
var requestManagerMock = new Mock<IRequestManager>();
41+
var currentRequestMock = new Mock<IRequestContext>();
4042

41-
requestManagerMock.Setup(m => m.GetUpdatedAttributes()).Returns(new Dictionary<AttrAttribute, object>());
43+
currentRequestMock.Setup(m => m.GetUpdatedAttributes()).Returns(new Dictionary<AttrAttribute, object>());
4244

4345
var jsonApiContextMock = new Mock<IJsonApiContext>();
4446
jsonApiContextMock.SetupAllProperties();
@@ -50,11 +52,11 @@ public JsonApiDeserializer_Benchmarks() {
5052
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
5153

5254

53-
_jsonApiDeSerializer = new JsonApiDeSerializer(jsonApiContextMock.Object, requestManagerMock.Object);
55+
_jsonApideserializer = new JsonApideserializer(jsonApiContextMock.Object, currentRequestMock.Object);
5456
}
5557

5658
[Benchmark]
57-
public object DeserializeSimpleObject() => _jsonApiDeSerializer.Deserialize<SimpleType>(Content);
59+
public object DeserializeSimpleObject() => _jsonApideserializer.Deserialize<SimpleType>(Content);
5860

5961
private class SimpleType : Identifiable {
6062
[Attr("name")]

benchmarks/Serialization/JsonApiSerializer_Benchmarks.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//using JsonApiDotNetCore.Internal.Generics;
77
//using JsonApiDotNetCore.Models;
88
//using JsonApiDotNetCore.Serialization;
9+
using JsonApiDotNetCore.Serialization.Contracts;
10+
911
//using JsonApiDotNetCore.Services;
1012
//using Moq;
1113
//using Newtonsoft.Json.Serialization;
@@ -34,7 +36,7 @@
3436

3537
// var genericProcessorFactoryMock = new Mock<IGenericProcessorFactory>();
3638

37-
// var documentBuilder = new DocumentBuilder(jsonApiContextMock.Object);
39+
// var documentBuilder = new BaseDocumentBuilder(jsonApiContextMock.Object);
3840
// _jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder);
3941
// }
4042

src/Examples/GettingStarted/Models/Article.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ public class Article : Identifiable
66
{
77
[Attr]
88
public string Title { get; set; }
9-
109
[HasOne]
1110
public Person Author { get; set; }
1211
public int AuthorId { get; set; }
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
using JsonApiDotNetCore.Configuration;
12
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Internal.Contracts;
24
using JsonApiDotNetCore.Services;
35
using JsonApiDotNetCoreExample.Models;
6+
using Microsoft.Extensions.Logging;
47

58
namespace JsonApiDotNetCoreExample.Controllers
69
{
710
public class PassportsController : JsonApiController<Passport>
811
{
9-
public PassportsController(
10-
IJsonApiContext jsonApiContext,
11-
IResourceService<Passport> resourceService)
12-
: base(jsonApiContext, resourceService)
13-
{ }
12+
public PassportsController(IJsonApiOptions jsonApiOptions, IResourceGraph resourceGraph, IResourceService<Passport, int> resourceService, ILoggerFactory loggerFactory = null) : base(jsonApiOptions, resourceGraph, resourceService, loggerFactory)
13+
{
14+
}
1415
}
1516
}

src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsCustomController.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,19 @@ namespace JsonApiDotNetCoreExample.Controllers
1313
public class TodoItemsCustomController : CustomJsonApiController<TodoItem>
1414
{
1515
public TodoItemsCustomController(
16-
IJsonApiContext jsonApiContext,
1716
IResourceService<TodoItem> resourceService,
1817
ILoggerFactory loggerFactory)
19-
: base(jsonApiContext, resourceService, loggerFactory)
18+
: base(resourceService, loggerFactory)
2019
{ }
2120
}
2221

2322
public class CustomJsonApiController<T>
2423
: CustomJsonApiController<T, int> where T : class, IIdentifiable<int>
2524
{
2625
public CustomJsonApiController(
27-
IJsonApiContext jsonApiContext,
2826
IResourceService<T, int> resourceService,
2927
ILoggerFactory loggerFactory)
30-
: base(jsonApiContext, resourceService, loggerFactory)
28+
: base(resourceService, loggerFactory)
3129
{ }
3230
}
3331

@@ -36,28 +34,23 @@ public class CustomJsonApiController<T, TId>
3634
{
3735
private readonly ILogger _logger;
3836
private readonly IResourceService<T, TId> _resourceService;
39-
private readonly IJsonApiContext _jsonApiContext;
4037

4138
protected IActionResult Forbidden()
4239
{
4340
return new StatusCodeResult(403);
4441
}
4542

4643
public CustomJsonApiController(
47-
IJsonApiContext jsonApiContext,
4844
IResourceService<T, TId> resourceService,
4945
ILoggerFactory loggerFactory)
5046
{
51-
_jsonApiContext = jsonApiContext.ApplyContext<T>(this);
5247
_resourceService = resourceService;
5348
_logger = loggerFactory.CreateLogger<JsonApiDotNetCore.Controllers.JsonApiController<T, TId>>();
5449
}
5550

5651
public CustomJsonApiController(
57-
IJsonApiContext jsonApiContext,
5852
IResourceService<T, TId> resourceService)
5953
{
60-
_jsonApiContext = jsonApiContext.ApplyContext<T>(this);
6154
_resourceService = resourceService;
6255
}
6356

@@ -102,8 +95,8 @@ public virtual async Task<IActionResult> PostAsync([FromBody] T entity)
10295
if (entity == null)
10396
return UnprocessableEntity();
10497

105-
if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId))
106-
return Forbidden();
98+
//if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId))
99+
// return Forbidden();
107100

108101
entity = await _resourceService.CreateAsync(entity);
109102

src/Examples/JsonApiDotNetCoreExample/Models/Person.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
using System;
21
using System.Collections.Generic;
32
using JsonApiDotNetCore.Models;
4-
using JsonApiDotNetCore.Services;
3+
using JsonApiDotNetCore.Models.Links;
54

65
namespace JsonApiDotNetCoreExample.Models
76
{
@@ -11,7 +10,7 @@ public class PersonRole : Identifiable
1110
public Person Person { get; set; }
1211
}
1312

14-
public class Person : Identifiable, IHasMeta, IIsLockable
13+
public class Person : Identifiable, IIsLockable
1514
{
1615
public bool IsLocked { get; set; }
1716

@@ -45,21 +44,13 @@ public class Person : Identifiable, IHasMeta, IIsLockable
4544
public virtual TodoItem StakeHolderTodo { get; set; }
4645
public virtual int? StakeHolderTodoId { get; set; }
4746

48-
[HasOne("unincludeable-item", documentLinks: Link.All, canInclude: false)]
47+
[HasOne("unincludeable-item", links: Link.All, canInclude: false)]
4948
public virtual TodoItem UnIncludeableItem { get; set; }
5049

5150
public int? PassportId { get; set; }
5251

5352
[HasOne("passport")]
5453
public virtual Passport Passport { get; set; }
5554

56-
public Dictionary<string, object> GetMeta(IJsonApiContext context)
57-
{
58-
return new Dictionary<string, object> {
59-
{ "copyright", "Copyright 2015 Example Corp." },
60-
{ "authors", new string[] { "Jared Nance" } }
61-
};
62-
}
63-
6455
}
6556
}

src/Examples/JsonApiDotNetCoreExample/Models/TodoItemCollection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ public class TodoItemCollection : Identifiable<Guid>
99
{
1010
[Attr("name")]
1111
public string Name { get; set; }
12-
public int OwnerId { get; set; }
1312

1413
[HasMany("todo-items")]
1514
public virtual List<TodoItem> TodoItems { get; set; }
1615

1716
[HasOne("owner")]
1817
public virtual Person Owner { get; set; }
18+
public int? OwnerId { get; set; }
1919
}
2020
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
using System;
12
using JsonApiDotNetCore.Models;
23

34
namespace JsonApiDotNetCoreExample.Models
45
{
56
public class User : Identifiable
67
{
7-
[Attr("username")] public string Username { get; set; }
8-
[Attr("password")] public string Password { get; set; }
8+
[Attr] public string Username { get; set; }
9+
[Attr] public string Password { get; set; }
910
}
1011
}

src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using JsonApiDotNetCore.Internal;
55
using JsonApiDotNetCore.Internal.Contracts;
66
using JsonApiDotNetCore.Models;
7+
using JsonApiDotNetCore.Services;
78
using JsonApiDotNetCoreExample.Models;
89

910
namespace JsonApiDotNetCoreExample.Resources

src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@
33
using JsonApiDotNetCore.Hooks;
44
using JsonApiDotNetCoreExample.Models;
55
using JsonApiDotNetCore.Internal.Contracts;
6+
using JsonApiDotNetCore.Models;
67

78
namespace JsonApiDotNetCoreExample.Resources
89
{
9-
public class PersonResource : LockableResource<Person>
10+
public class PersonResource : LockableResource<Person>, IHasMeta
1011
{
1112
public PersonResource(IResourceGraph graph) : base(graph) { }
1213

14+
public override IEnumerable<Person> BeforeUpdate(IDiffableEntityHashSet<Person> entities, ResourcePipeline pipeline)
15+
{
16+
return base.BeforeUpdate(entities, pipeline);
17+
}
18+
1319
public override IEnumerable<string> BeforeUpdateRelationship(HashSet<string> ids, IRelationshipsDictionary<Person> entitiesByRelationship, ResourcePipeline pipeline)
1420
{
1521
BeforeImplicitUpdateRelationship(entitiesByRelationship, pipeline);
@@ -20,5 +26,13 @@ public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<P
2026
{
2127
entitiesByRelationship.GetByRelationship<Passport>().ToList().ForEach(kvp => DisallowLocked(kvp.Value));
2228
}
29+
30+
public Dictionary<string, object> GetMeta()
31+
{
32+
return new Dictionary<string, object> {
33+
{ "copyright", "Copyright 2015 Example Corp." },
34+
{ "authors", new string[] { "Jared Nance", "Maurits Moeys" } }
35+
};
36+
}
2337
}
2438
}

src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
using System.Collections.Generic;
21
using System.Linq;
32
using JsonApiDotNetCore.Models;
43
using JsonApiDotNetCoreExample.Models;
54
using JsonApiDotNetCore.Internal.Query;
65
using JsonApiDotNetCore.Internal.Contracts;
7-
6+
using JsonApiDotNetCore.Services;
87

98
namespace JsonApiDotNetCoreExample.Resources
109
{
1110
public class UserResource : ResourceDefinition<User>
1211
{
13-
public UserResource(IResourceGraph graph) : base(graph) { }
14-
15-
protected override List<AttrAttribute> OutputAttrs()
16-
=> Remove(user => user.Password);
12+
public UserResource(IResourceGraph graph, IFieldsExplorer fieldExplorer) : base(fieldExplorer, graph)
13+
{
14+
HideFields(u => u.Password);
15+
}
1716

1817
public override QueryFilters GetQueryFilters()
1918
{

src/Examples/JsonApiDotNetCoreExample/Services/CustomArticleService.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using JsonApiDotNetCore.Internal;
55
using JsonApiDotNetCore.Internal.Contracts;
66
using JsonApiDotNetCore.Managers.Contracts;
7+
using JsonApiDotNetCore.Models;
8+
using JsonApiDotNetCore.Query;
9+
using JsonApiDotNetCore.Serialization;
710
using JsonApiDotNetCore.Services;
811
using JsonApiDotNetCoreExample.Models;
912
using Microsoft.Extensions.Logging;
@@ -13,16 +16,9 @@ namespace JsonApiDotNetCoreExample.Services
1316
{
1417
public class CustomArticleService : EntityResourceService<Article>
1518
{
16-
public CustomArticleService(
17-
IEntityRepository<Article> repository,
18-
IJsonApiOptions jsonApiOptions,
19-
IRequestManager queryManager,
20-
IPageManager pageManager,
21-
IResourceGraph resourceGraph,
22-
IResourceHookExecutor resourceHookExecutor = null,
23-
ILoggerFactory loggerFactory = null
24-
) : base(repository: repository, jsonApiOptions, queryManager, pageManager, resourceGraph:resourceGraph, loggerFactory, resourceHookExecutor)
25-
{ }
19+
public CustomArticleService(IEntityRepository<Article, int> repository, IJsonApiOptions options, ITargetedFields updatedFields, ICurrentRequest currentRequest, IIncludeService includeService, ISparseFieldsService sparseFieldsService, IPageQueryService pageManager, IResourceGraph resourceGraph, IResourceHookExecutor hookExecutor = null, IResourceMapper mapper = null, ILoggerFactory loggerFactory = null) : base(repository, options, updatedFields, currentRequest, includeService, sparseFieldsService, pageManager, resourceGraph, hookExecutor, mapper, loggerFactory)
20+
{
21+
}
2622

2723
public override async Task<Article> GetAsync(int id)
2824
{

src/Examples/JsonApiDotNetCoreExample/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
3737
options.DefaultPageSize = 5;
3838
options.IncludeTotalRecordCount = true;
3939
options.EnableResourceHooks = true;
40-
options.LoadDatabaseValues = true;
40+
options.LoaDatabaseValues = true;
4141
},
4242
discovery => discovery.AddCurrentAssembly());
4343

src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public Task<object> GetRelationshipAsync(int id, string relationshipName)
5959
throw new NotImplementedException();
6060
}
6161

62-
public Task<object> GetRelationshipsAsync(int id, string relationshipName)
62+
public Task<TodoItem> GetRelationshipsAsync(int id, string relationshipName)
6363
{
6464
throw new NotImplementedException();
6565
}
@@ -84,7 +84,7 @@ public Task<TodoItem> UpdateAsync(int id, TodoItem entity)
8484
throw new NotImplementedException();
8585
}
8686

87-
public Task UpdateRelationshipsAsync(int id, string relationshipName, List<ResourceObject> relationships)
87+
public Task UpdateRelationshipsAsync(int id, string relationshipName, object relationships)
8888
{
8989
throw new NotImplementedException();
9090
}

0 commit comments

Comments
 (0)