Skip to content

Commit 446cba4

Browse files
authored
Feat/context decoupling (#557)
* 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
1 parent cc46a60 commit 446cba4

File tree

150 files changed

+2699
-1722
lines changed

Some content is hidden

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

150 files changed

+2699
-1722
lines changed

Directory.Build.props

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
<NetCoreAppVersion>netcoreapp2.0</NetCoreAppVersion>
55
<NetStandardVersion>netstandard2.0</NetStandardVersion>
66

7-
<AspNetCoreVersion>2.1.0</AspNetCoreVersion>
7+
<AspNetCoreVersion>2.*</AspNetCoreVersion>
88

9-
<MicrosoftLoggingVersion>2.1.0</MicrosoftLoggingVersion>
10-
<MicrosoftConfigurationVersion>2.1.0</MicrosoftConfigurationVersion>
11-
<MicrosoftOptionsVersion>2.1.0</MicrosoftOptionsVersion>
9+
<MicrosoftLoggingVersion>2.*</MicrosoftLoggingVersion>
10+
<MicrosoftConfigurationVersion>2.*</MicrosoftConfigurationVersion>
11+
<MicrosoftOptionsVersion>2.*</MicrosoftOptionsVersion>
1212

13-
<EFCoreVersion>2.1.0</EFCoreVersion>
14-
<EFCoreToolsVersion>2.1.0</EFCoreToolsVersion>
13+
<EFCoreVersion>2.*</EFCoreVersion>
14+
<EFCoreToolsVersion>2.*</EFCoreToolsVersion>
1515

1616
<NpgsqlVersion>4.0.0</NpgsqlVersion>
1717
<NpgsqlPostgreSQLVersion>2.1.0</NpgsqlPostgreSQLVersion>

JsonApiDotnetCore.sln

Lines changed: 188 additions & 188 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ See [the documentation](https://json-api-dotnet.github.io/#/) for detailed usage
4242

4343
```csharp
4444
public class Article : Identifiable
45-
{
45+
{
4646
[Attr("name")]
4747
public string Name { get; set; }
4848
}
@@ -91,7 +91,7 @@ Running tests locally requires access to a postgresql database.
9191
If you have docker installed, this can be propped up via:
9292

9393
```bash
94-
docker run --rm --name jsonapi-dotnet-core-testing -e POSTGRES_DB=JsonApiDotNetCoreExample -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres
94+
docker run --rm --name jsonapi-dotnet-core-testing -e POSTGRES_DB=JsonApiDotNetCoreExample -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres
9595
```
9696

9797
And then to run the tests:

benchmarks/LinkBuilder/LinkBuilder_ GetNamespaceFromPath_Benchmarks.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using BenchmarkDotNet.Attributes;
22
using BenchmarkDotNet.Attributes.Exporters;
33
using BenchmarkDotNet.Attributes.Jobs;
4+
using System;
45

56
namespace Benchmarks.LinkBuilder
67
{
7-
[MarkdownExporter, SimpleJob(launchCount : 3, warmupCount : 10, targetCount : 20), MemoryDiagnoser]
8+
[MarkdownExporter, SimpleJob(launchCount: 3, warmupCount: 10, targetCount: 20), MemoryDiagnoser]
89
public class LinkBuilder_GetNamespaceFromPath_Benchmarks
910
{
1011
private const string PATH = "/api/some-really-long-namespace-path/resources/current/articles";
@@ -14,7 +15,7 @@ public class LinkBuilder_GetNamespaceFromPath_Benchmarks
1415
public void UsingSplit() => GetNamespaceFromPath_BySplitting(PATH, ENTITY_NAME);
1516

1617
[Benchmark]
17-
public void Current() => GetNameSpaceFromPath_Current(PATH, ENTITY_NAME);
18+
public void Current() => GetNameSpaceFromPathCurrent(PATH, ENTITY_NAME);
1819

1920
public static string GetNamespaceFromPath_BySplitting(string path, string entityName)
2021
{
@@ -32,7 +33,38 @@ public static string GetNamespaceFromPath_BySplitting(string path, string entity
3233
return nSpace;
3334
}
3435

35-
public static string GetNameSpaceFromPath_Current(string path, string entityName)
36-
=> JsonApiDotNetCore.Builders.LinkBuilder.GetNamespaceFromPath(path, entityName);
36+
public static string GetNameSpaceFromPathCurrent(string path, string entityName)
37+
{
38+
39+
var entityNameSpan = entityName.AsSpan();
40+
var pathSpan = path.AsSpan();
41+
const char delimiter = '/';
42+
for (var i = 0; i < pathSpan.Length; i++)
43+
{
44+
if (pathSpan[i].Equals(delimiter))
45+
{
46+
var nextPosition = i + 1;
47+
if (pathSpan.Length > i + entityNameSpan.Length)
48+
{
49+
var possiblePathSegment = pathSpan.Slice(nextPosition, entityNameSpan.Length);
50+
if (entityNameSpan.SequenceEqual(possiblePathSegment))
51+
{
52+
// check to see if it's the last position in the string
53+
// or if the next character is a /
54+
var lastCharacterPosition = nextPosition + entityNameSpan.Length;
55+
56+
if (lastCharacterPosition == pathSpan.Length || pathSpan.Length >= lastCharacterPosition + 2 && pathSpan[lastCharacterPosition].Equals(delimiter))
57+
{
58+
return pathSpan.Slice(0, i).ToString();
59+
}
60+
}
61+
}
62+
}
63+
}
64+
65+
return string.Empty;
66+
67+
68+
}
3769
}
3870
}

benchmarks/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using BenchmarkDotNet.Running;
1+
using BenchmarkDotNet.Running;
22
using Benchmarks.JsonApiContext;
33
using Benchmarks.LinkBuilder;
44
using Benchmarks.Query;
@@ -10,7 +10,7 @@ class Program {
1010
static void Main(string[] args) {
1111
var switcher = new BenchmarkSwitcher(new[] {
1212
typeof(JsonApiDeserializer_Benchmarks),
13-
typeof(JsonApiSerializer_Benchmarks),
13+
//typeof(JsonApiSerializer_Benchmarks),
1414
typeof(QueryParser_Benchmarks),
1515
typeof(LinkBuilder_GetNamespaceFromPath_Benchmarks),
1616
typeof(ContainsMediaTypeParameters_Benchmarks),

benchmarks/Query/QueryParser_Benchmarks.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using BenchmarkDotNet.Attributes.Jobs;
66
using JsonApiDotNetCore.Configuration;
77
using JsonApiDotNetCore.Internal;
8+
using JsonApiDotNetCore.Managers.Contracts;
89
using JsonApiDotNetCore.Models;
910
using JsonApiDotNetCore.Services;
1011
using Microsoft.AspNetCore.Http.Internal;
@@ -21,14 +22,14 @@ public class QueryParser_Benchmarks {
2122
private const string DESCENDING_SORT = "-" + ATTRIBUTE;
2223

2324
public QueryParser_Benchmarks() {
24-
var controllerContextMock = new Mock<IControllerContext>();
25-
controllerContextMock.Setup(m => m.RequestEntity).Returns(new ContextEntity {
25+
var requestMock = new Mock<IRequestManager>();
26+
requestMock.Setup(m => m.GetContextEntity()).Returns(new ContextEntity {
2627
Attributes = new List<AttrAttribute> {
2728
new AttrAttribute(ATTRIBUTE, ATTRIBUTE)
2829
}
2930
});
3031
var options = new JsonApiOptions();
31-
_queryParser = new BenchmarkFacade(controllerContextMock.Object, options);
32+
_queryParser = new BenchmarkFacade(requestMock.Object, options);
3233
}
3334

3435
[Benchmark]
@@ -58,8 +59,8 @@ private void Run(int iterations, Action action) {
5859
// this facade allows us to expose and micro-benchmark protected methods
5960
private class BenchmarkFacade : QueryParser {
6061
public BenchmarkFacade(
61-
IControllerContext controllerContext,
62-
JsonApiOptions options) : base(controllerContext, options) { }
62+
IRequestManager requestManager,
63+
JsonApiOptions options) : base(requestManager, options) { }
6364

6465
public void _ParseSortParameters(string value) => base.ParseSortParameters(value);
6566
}

benchmarks/Serialization/JsonApiDeserializer_Benchmarks.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
using BenchmarkDotNet.Attributes.Exporters;
55
using JsonApiDotNetCore.Builders;
66
using JsonApiDotNetCore.Configuration;
7-
using JsonApiDotNetCore.Internal.Generics;
7+
using JsonApiDotNetCore.Managers.Contracts;
88
using JsonApiDotNetCore.Models;
99
using JsonApiDotNetCore.Serialization;
1010
using JsonApiDotNetCore.Services;
1111
using Moq;
1212
using Newtonsoft.Json;
1313
using Newtonsoft.Json.Serialization;
1414

15-
namespace Benchmarks.Serialization {
15+
namespace Benchmarks.Serialization
16+
{
1617
[MarkdownExporter]
1718
public class JsonApiDeserializer_Benchmarks {
1819
private const string TYPE_NAME = "simple-types";
@@ -35,18 +36,21 @@ public JsonApiDeserializer_Benchmarks() {
3536
var resourceGraphBuilder = new ResourceGraphBuilder();
3637
resourceGraphBuilder.AddResource<SimpleType>(TYPE_NAME);
3738
var resourceGraph = resourceGraphBuilder.Build();
39+
var requestManagerMock = new Mock<IRequestManager>();
40+
41+
requestManagerMock.Setup(m => m.GetUpdatedAttributes()).Returns(new Dictionary<AttrAttribute, object>());
3842

3943
var jsonApiContextMock = new Mock<IJsonApiContext>();
4044
jsonApiContextMock.SetupAllProperties();
4145
jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
42-
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
46+
jsonApiContextMock.Setup(m => m.RequestManager.GetUpdatedAttributes()).Returns(new Dictionary<AttrAttribute, object>());
4347

4448
var jsonApiOptions = new JsonApiOptions();
4549
jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
4650
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
4751

4852

49-
_jsonApiDeSerializer = new JsonApiDeSerializer(jsonApiContextMock.Object);
53+
_jsonApiDeSerializer = new JsonApiDeSerializer(jsonApiContextMock.Object, requestManagerMock.Object);
5054
}
5155

5256
[Benchmark]
Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,49 @@
1-
using System.Collections.Generic;
2-
using BenchmarkDotNet.Attributes;
3-
using BenchmarkDotNet.Attributes.Exporters;
4-
using JsonApiDotNetCore.Builders;
5-
using JsonApiDotNetCore.Configuration;
6-
using JsonApiDotNetCore.Internal.Generics;
7-
using JsonApiDotNetCore.Models;
8-
using JsonApiDotNetCore.Serialization;
9-
using JsonApiDotNetCore.Services;
10-
using Moq;
11-
using Newtonsoft.Json.Serialization;
12-
13-
namespace Benchmarks.Serialization {
14-
[MarkdownExporter]
15-
public class JsonApiSerializer_Benchmarks {
16-
private const string TYPE_NAME = "simple-types";
17-
private static readonly SimpleType Content = new SimpleType();
18-
19-
private readonly JsonApiSerializer _jsonApiSerializer;
20-
21-
public JsonApiSerializer_Benchmarks() {
22-
var resourceGraphBuilder = new ResourceGraphBuilder();
23-
resourceGraphBuilder.AddResource<SimpleType>(TYPE_NAME);
24-
var resourceGraph = resourceGraphBuilder.Build();
25-
26-
var jsonApiContextMock = new Mock<IJsonApiContext>();
27-
jsonApiContextMock.SetupAllProperties();
28-
jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
29-
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
30-
31-
var jsonApiOptions = new JsonApiOptions();
32-
jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
33-
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
34-
35-
var genericProcessorFactoryMock = new Mock<IGenericProcessorFactory>();
36-
37-
var documentBuilder = new DocumentBuilder(jsonApiContextMock.Object);
38-
_jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder);
39-
}
40-
41-
[Benchmark]
42-
public object SerializeSimpleObject() => _jsonApiSerializer.Serialize(Content);
43-
44-
private class SimpleType : Identifiable {
45-
[Attr("name")]
46-
public string Name { get; set; }
47-
}
48-
}
49-
}
1+
//using System.Collections.Generic;
2+
//using BenchmarkDotNet.Attributes;
3+
//using BenchmarkDotNet.Attributes.Exporters;
4+
//using JsonApiDotNetCore.Builders;
5+
//using JsonApiDotNetCore.Configuration;
6+
//using JsonApiDotNetCore.Internal.Generics;
7+
//using JsonApiDotNetCore.Models;
8+
//using JsonApiDotNetCore.Serialization;
9+
//using JsonApiDotNetCore.Services;
10+
//using Moq;
11+
//using Newtonsoft.Json.Serialization;
12+
13+
//namespace Benchmarks.Serialization {
14+
// [MarkdownExporter]
15+
// public class JsonApiSerializer_Benchmarks {
16+
// private const string TYPE_NAME = "simple-types";
17+
// private static readonly SimpleType Content = new SimpleType();
18+
19+
// private readonly JsonApiSerializer _jsonApiSerializer;
20+
21+
// public JsonApiSerializer_Benchmarks() {
22+
// var resourceGraphBuilder = new ResourceGraphBuilder();
23+
// resourceGraphBuilder.AddResource<SimpleType>(TYPE_NAME);
24+
// var resourceGraph = resourceGraphBuilder.Build();
25+
26+
// var jsonApiContextMock = new Mock<IJsonApiContext>();
27+
// jsonApiContextMock.SetupAllProperties();
28+
// jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph);
29+
// jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
30+
31+
// var jsonApiOptions = new JsonApiOptions();
32+
// jsonApiOptions.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
33+
// jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
34+
35+
// var genericProcessorFactoryMock = new Mock<IGenericProcessorFactory>();
36+
37+
// var documentBuilder = new DocumentBuilder(jsonApiContextMock.Object);
38+
// _jsonApiSerializer = new JsonApiSerializer(jsonApiContextMock.Object, documentBuilder);
39+
// }
40+
41+
// [Benchmark]
42+
// public object SerializeSimpleObject() => _jsonApiSerializer.Serialize(Content);
43+
44+
// private class SimpleType : Identifiable {
45+
// [Attr("name")]
46+
// public string Name { get; set; }
47+
// }
48+
// }
49+
//}

docs/obsoletes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# For v5
2+
3+
* Anything to do with JsonApiContext, make it internal and fix anything related to it.

markdownlint.config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"MD033": {
3+
"allowed_elements": [ "p", "img", "p" ]
4+
}
5+
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using GettingStarted.Models;
2+
using JsonApiDotNetCore.Configuration;
23
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Internal.Contracts;
35
using JsonApiDotNetCore.Services;
46

57
namespace GettingStarted
68
{
79
public class ArticlesController : JsonApiController<Article>
810
{
911
public ArticlesController(
10-
IJsonApiContext jsonApiContext,
11-
IResourceService<Article> resourceService)
12-
: base(jsonApiContext, resourceService)
12+
IJsonApiOptions jsonApiOptions,
13+
IResourceGraph resourceGraph,
14+
IResourceService<Article> resourceService)
15+
: base(jsonApiOptions, resourceGraph, resourceService)
1316
{ }
1417
}
15-
}
18+
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using GettingStarted.Models;
2+
using JsonApiDotNetCore.Configuration;
23
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Internal.Contracts;
35
using JsonApiDotNetCore.Services;
46

57
namespace GettingStarted
68
{
79
public class PeopleController : JsonApiController<Person>
810
{
911
public PeopleController(
10-
IJsonApiContext jsonApiContext,
11-
IResourceService<Person> resourceService)
12-
: base(jsonApiContext, resourceService)
12+
IJsonApiOptions jsonApiOptions,
13+
IResourceGraph resourceGraph,
14+
IResourceService<Person> resourceService)
15+
: base(jsonApiOptions, resourceGraph, resourceService)
1316
{ }
1417
}
15-
}
18+
}

src/Examples/GettingStarted/GettingStarted.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17-
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
18-
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
19-
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
17+
<PackageReference Include="Microsoft.AspNetCore" Version="$(AspNetCoreVersion)" />
18+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(EFCoreVersion)" />
19+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EFCoreVersion)" />
2020
</ItemGroup>
2121

2222
</Project>
Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
11
{
2-
"iisSettings": {
3-
"windowsAuthentication": false,
4-
"anonymousAuthentication": true,
5-
"iisExpress": {
6-
"applicationUrl": "http://localhost:56042/",
7-
"sslPort": 0
8-
}
9-
},
102
"profiles": {
11-
"IIS Express": {
12-
"commandName": "IISExpress",
13-
"launchBrowser": true,
14-
"environmentVariables": {
15-
"ASPNETCORE_ENVIRONMENT": "Development"
16-
}
17-
},
183
"GettingStarted": {
194
"commandName": "Project",
205
"launchBrowser": true,
21-
"environmentVariables": {}
6+
"environmentVariables": {
7+
"ASPNETCORE_ENVIRONMENT": "Development"
8+
},
9+
"applicationUrl": "https://localhost:5001;http://localhost:5000"
2210
}
2311
}
2412
}

0 commit comments

Comments
 (0)