Skip to content

Commit 271693a

Browse files
authored
Merge pull request #330 from json-api-dotnet/develop
v2.3.4 Introduce ResourceDefinition
2 parents 1a14773 + d67a894 commit 271693a

File tree

51 files changed

+695
-92
lines changed

Some content is hidden

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

51 files changed

+695
-92
lines changed

Diff for: Build.ps1

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,18 @@ If($env:APPVEYOR_REPO_TAG -eq $true) {
4646

4747
IF ([string]::IsNullOrWhitespace($revision)){
4848
Write-Output "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts"
49-
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts
49+
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --include-symbols
5050
CheckLastExitCode
5151
}
5252
Else {
5353
Write-Output "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$revision"
54-
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$revision
54+
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$revision --include-symbols
5555
CheckLastExitCode
5656
}
5757
}
5858
Else {
5959
Write-Output "VERSION-SUFFIX: alpha1-$revision"
6060
Write-Output "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision"
61-
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision
61+
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision --include-symbols
6262
CheckLastExitCode
6363
}

Diff for: appveyor.yml

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ deploy:
4444
api_key:
4545
secure: 6CeYcZ4Ze+57gxfeuHzqP6ldbUkPtF6pfpVM1Gw/K2jExFrAz763gNAQ++tiacq3
4646
skip_symbols: false
47+
symbol_server: https://www.myget.org/F/research-institute/symbols/api/v2/package
4748
on:
4849
branch: develop
4950
- provider: NuGet
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using JsonApiDotNetCore.Controllers;
2+
using JsonApiDotNetCore.Services;
3+
using JsonApiDotNetCoreExample.Models;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace JsonApiDotNetCoreExample.Controllers
7+
{
8+
public class UsersController : JsonApiController<User>
9+
{
10+
public UsersController(
11+
IJsonApiContext jsonApiContext,
12+
IResourceService<User> resourceService,
13+
ILoggerFactory loggerFactory)
14+
: base(jsonApiContext, resourceService, loggerFactory)
15+
{ }
16+
}
17+
}

Diff for: src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
3737

3838
public DbSet<Article> Articles { get; set; }
3939
public DbSet<Author> Authors { get; set; }
40-
4140
public DbSet<NonJsonApiResource> NonJsonApiResources { get; set; }
41+
public DbSet<User> Users { get; set; }
4242
}
4343
}

Diff for: src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
using Microsoft.EntityFrameworkCore.Metadata;
21
using Microsoft.EntityFrameworkCore.Migrations;
32
using System;
4-
using System.Collections.Generic;
53
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
64

75
namespace JsonApiDotNetCoreExample.Migrations

Diff for: src/Examples/JsonApiDotNetCoreExample/Models/User.cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Models;
2+
3+
namespace JsonApiDotNetCoreExample.Models
4+
{
5+
public class User : Identifiable
6+
{
7+
[Attr("username")] public string Username { get; set; }
8+
[Attr("password")] public string Password { get; set; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Models;
3+
using JsonApiDotNetCoreExample.Models;
4+
5+
namespace JsonApiDotNetCoreExample.Resources
6+
{
7+
public class UserResource : ResourceDefinition<User>
8+
{
9+
protected override List<AttrAttribute> OutputAttrs()
10+
=> Remove(user => user.Password);
11+
}
12+
}

Diff for: src/Examples/JsonApiDotNetCoreExample/Startup.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
using Microsoft.EntityFrameworkCore;
88
using JsonApiDotNetCore.Extensions;
99
using System;
10+
using JsonApiDotNetCore.Models;
11+
using JsonApiDotNetCoreExample.Resources;
12+
using JsonApiDotNetCoreExample.Models;
1013

1114
namespace JsonApiDotNetCoreExample
1215
{
@@ -38,7 +41,9 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
3841
options.Namespace = "api/v1";
3942
options.DefaultPageSize = 5;
4043
options.IncludeTotalRecordCount = true;
41-
});
44+
})
45+
// TODO: this should be handled via auto-discovery
46+
.AddScoped<ResourceDefinition<User>, UserResource>();
4247

4348
var provider = services.BuildServiceProvider();
4449
var appContext = provider.GetRequiredService<AppDbContext>();

Diff for: src/Examples/ReportsExample/Controllers/ReportsController.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
52
using Microsoft.AspNetCore.Mvc;
63
using JsonApiDotNetCore.Controllers;
74
using JsonApiDotNetCore.Services;

Diff for: src/Examples/ReportsExample/Services/ReportService.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections.Generic;
32
using System.Threading.Tasks;
43
using JsonApiDotNetCore.Services;

Diff for: src/JsonApiDotNetCore/Builders/ContextGraphBuilder.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public IContextGraph Build()
5959
_entities.ForEach(e => e.Links = GetLinkFlags(e.EntityType));
6060

6161
var graph = new ContextGraph(_entities, _usesDbContext, _validationResults);
62-
6362
return graph;
6463
}
6564

@@ -83,7 +82,8 @@ public IContextGraphBuilder AddResource<TResource, TId>(string pluralizedTypeNam
8382
EntityType = entityType,
8483
IdentityType = idType,
8584
Attributes = GetAttributes(entityType),
86-
Relationships = GetRelationships(entityType)
85+
Relationships = GetRelationships(entityType),
86+
ResourceType = GetResourceDefinitionType(entityType)
8787
};
8888

8989
private Link GetLinkFlags(Type entityType)
@@ -104,8 +104,12 @@ protected virtual List<AttrAttribute> GetAttributes(Type entityType)
104104
foreach (var prop in properties)
105105
{
106106
var attribute = (AttrAttribute)prop.GetCustomAttribute(typeof(AttrAttribute));
107-
if (attribute == null) continue;
107+
if (attribute == null)
108+
continue;
109+
108110
attribute.InternalAttributeName = prop.Name;
111+
attribute.PropertyInfo = prop;
112+
109113
attributes.Add(attribute);
110114
}
111115
return attributes;
@@ -136,6 +140,8 @@ protected virtual Type GetRelationshipType(RelationshipAttribute relation, Prope
136140
return prop.PropertyType;
137141
}
138142

143+
private Type GetResourceDefinitionType(Type entityType) => typeof(ResourceDefinition<>).MakeGenericType(entityType);
144+
139145
public IContextGraphBuilder AddDbContext<T>() where T : DbContext
140146
{
141147
_usesDbContext = true;

Diff for: src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
@@ -14,22 +15,29 @@ public class DocumentBuilder : IDocumentBuilder
1415
private readonly IContextGraph _contextGraph;
1516
private readonly IRequestMeta _requestMeta;
1617
private readonly DocumentBuilderOptions _documentBuilderOptions;
18+
private readonly IScopedServiceProvider _scopedServiceProvider;
1719

18-
public DocumentBuilder(IJsonApiContext jsonApiContext, IRequestMeta requestMeta = null, IDocumentBuilderOptionsProvider documentBuilderOptionsProvider = null)
20+
public DocumentBuilder(
21+
IJsonApiContext jsonApiContext,
22+
IRequestMeta requestMeta = null,
23+
IDocumentBuilderOptionsProvider documentBuilderOptionsProvider = null,
24+
IScopedServiceProvider scopedServiceProvider = null)
1925
{
2026
_jsonApiContext = jsonApiContext;
2127
_contextGraph = jsonApiContext.ContextGraph;
2228
_requestMeta = requestMeta;
23-
_documentBuilderOptions = documentBuilderOptionsProvider?.GetDocumentBuilderOptions() ?? new DocumentBuilderOptions(); ;
29+
_documentBuilderOptions = documentBuilderOptionsProvider?.GetDocumentBuilderOptions() ?? new DocumentBuilderOptions();
30+
_scopedServiceProvider = scopedServiceProvider;
2431
}
2532

2633
public Document Build(IIdentifiable entity)
2734
{
2835
var contextEntity = _contextGraph.GetContextEntity(entity.GetType());
2936

37+
var resourceDefinition = _scopedServiceProvider?.GetService(contextEntity.ResourceType) as IResourceDefinition;
3038
var document = new Document
3139
{
32-
Data = GetData(contextEntity, entity),
40+
Data = GetData(contextEntity, entity, resourceDefinition),
3341
Meta = GetMeta(entity)
3442
};
3543

@@ -44,8 +52,8 @@ public Document Build(IIdentifiable entity)
4452
public Documents Build(IEnumerable<IIdentifiable> entities)
4553
{
4654
var entityType = entities.GetElementType();
47-
4855
var contextEntity = _contextGraph.GetContextEntity(entityType);
56+
var resourceDefinition = _scopedServiceProvider?.GetService(contextEntity.ResourceType) as IResourceDefinition;
4957

5058
var enumeratedEntities = entities as IList<IIdentifiable> ?? entities.ToList();
5159
var documents = new Documents
@@ -59,7 +67,7 @@ public Documents Build(IEnumerable<IIdentifiable> entities)
5967

6068
foreach (var entity in enumeratedEntities)
6169
{
62-
documents.Data.Add(GetData(contextEntity, entity));
70+
documents.Data.Add(GetData(contextEntity, entity, resourceDefinition));
6371
documents.Included = AppendIncludedObject(documents.Included, contextEntity, entity);
6472
}
6573

@@ -98,7 +106,11 @@ private List<DocumentData> AppendIncludedObject(List<DocumentData> includedObjec
98106
return includedObject;
99107
}
100108

109+
[Obsolete("You should specify an IResourceDefinition implementation using the GetData/3 overload.")]
101110
public DocumentData GetData(ContextEntity contextEntity, IIdentifiable entity)
111+
=> GetData(contextEntity, entity, resourceDefinition: null);
112+
113+
public DocumentData GetData(ContextEntity contextEntity, IIdentifiable entity, IResourceDefinition resourceDefinition = null)
102114
{
103115
var data = new DocumentData
104116
{
@@ -111,7 +123,8 @@ public DocumentData GetData(ContextEntity contextEntity, IIdentifiable entity)
111123

112124
data.Attributes = new Dictionary<string, object>();
113125

114-
contextEntity.Attributes.ForEach(attr =>
126+
var resourceAttributes = resourceDefinition?.GetOutputAttrs(entity) ?? contextEntity.Attributes;
127+
resourceAttributes.ForEach(attr =>
115128
{
116129
var attributeValue = attr.GetValue(entity);
117130
if (ShouldIncludeAttribute(attr, attributeValue))
@@ -219,8 +232,9 @@ private DocumentData GetIncludedEntity(IIdentifiable entity)
219232
if (entity == null) return null;
220233

221234
var contextEntity = _jsonApiContext.ContextGraph.GetContextEntity(entity.GetType());
235+
var resourceDefinition = _scopedServiceProvider.GetService(contextEntity.ResourceType) as IResourceDefinition;
222236

223-
var data = GetData(contextEntity, entity);
237+
var data = GetData(contextEntity, entity, resourceDefinition);
224238

225239
data.Attributes = new Dictionary<string, object>();
226240

Diff for: src/JsonApiDotNetCore/Builders/IDocumentBuilder.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using JsonApiDotNetCore.Internal;
34
using JsonApiDotNetCore.Models;
@@ -8,6 +9,9 @@ public interface IDocumentBuilder
89
{
910
Document Build(IIdentifiable entity);
1011
Documents Build(IEnumerable<IIdentifiable> entities);
12+
13+
[Obsolete("You should specify an IResourceDefinition implementation using the GetData/3 overload.")]
1114
DocumentData GetData(ContextEntity contextEntity, IIdentifiable entity);
15+
DocumentData GetData(ContextEntity contextEntity, IIdentifiable entity, IResourceDefinition resourceDefinition = null);
1216
}
13-
}
17+
}

Diff for: src/JsonApiDotNetCore/Controllers/JsonApiCmdController.cs

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using JsonApiDotNetCore.Models;
44
using JsonApiDotNetCore.Services;
55
using Microsoft.AspNetCore.Mvc;
6-
using Microsoft.Extensions.Logging;
76

87
namespace JsonApiDotNetCore.Controllers
98
{

Diff for: src/JsonApiDotNetCore/Controllers/JsonApiQueryController.cs

-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using System.Collections.Generic;
21
using System.Threading.Tasks;
32
using JsonApiDotNetCore.Models;
43
using JsonApiDotNetCore.Services;
54
using Microsoft.AspNetCore.Mvc;
6-
using Microsoft.Extensions.Logging;
75

86
namespace JsonApiDotNetCore.Controllers
97
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Threading;
3+
4+
namespace JsonApiDotNetCore.DependencyInjection
5+
{
6+
internal class ServiceLocator
7+
{
8+
public static AsyncLocal<IServiceProvider> _scopedProvider = new AsyncLocal<IServiceProvider>();
9+
public static void Initialize(IServiceProvider serviceProvider) => _scopedProvider.Value = serviceProvider;
10+
11+
public static object GetService(Type type)
12+
=> _scopedProvider.Value != null
13+
? _scopedProvider.Value.GetService(type)
14+
: throw new InvalidOperationException(
15+
$"Service locator has not been initialized for the current asynchronous flow. Call {nameof(Initialize)} first."
16+
);
17+
}
18+
}

Diff for: src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Linq;
3-
using JsonApiDotNetCore.Internal;
43
using JsonApiDotNetCore.Models;
54
using Microsoft.EntityFrameworkCore;
65

Diff for: src/JsonApiDotNetCore/Internal/ContextEntity.cs

+29
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,40 @@ namespace JsonApiDotNetCore.Internal
66
{
77
public class ContextEntity
88
{
9+
/// <summary>
10+
/// The exposed resource name
11+
/// </summary>
912
public string EntityName { get; set; }
13+
14+
/// <summary>
15+
/// The data model type
16+
/// </summary>
1017
public Type EntityType { get; set; }
18+
19+
/// <summary>
20+
/// The identity member type
21+
/// </summary>
1122
public Type IdentityType { get; set; }
23+
24+
/// <summary>
25+
/// The concrete <see cref="ResourceDefinition{T}"/> type.
26+
/// We store this so that we don't need to re-compute the generic type.
27+
/// </summary>
28+
public Type ResourceType { get; set; }
29+
30+
/// <summary>
31+
/// Exposed resource attributes
32+
/// </summary>
1233
public List<AttrAttribute> Attributes { get; set; }
34+
35+
/// <summary>
36+
/// Exposed resource relationships
37+
/// </summary>
1338
public List<RelationshipAttribute> Relationships { get; set; }
39+
40+
/// <summary>
41+
/// Links to include in resource responses
42+
/// </summary>
1443
public Link Links { get; set; } = Link.All;
1544
}
1645
}

Diff for: src/JsonApiDotNetCore/Internal/ContextGraph.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ public class ContextGraph : IContextGraph
1717
{
1818
internal List<ContextEntity> Entities { get; }
1919
internal List<ValidationResult> ValidationResults { get; }
20+
internal static IContextGraph Instance { get; set; }
2021

2122
public ContextGraph() { }
22-
2323
public ContextGraph(List<ContextEntity> entities, bool usesDbContext)
2424
{
2525
Entities = entities;
2626
UsesDbContext = usesDbContext;
2727
ValidationResults = new List<ValidationResult>();
28+
Instance = this;
2829
}
2930

3031
// eventually, this is the planned public constructor
@@ -36,6 +37,7 @@ internal ContextGraph(List<ContextEntity> entities, bool usesDbContext, List<Val
3637
Entities = entities;
3738
UsesDbContext = usesDbContext;
3839
ValidationResults = validationResults;
40+
Instance = this;
3941
}
4042

4143
public bool UsesDbContext { get; }

Diff for: src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Linq;
33
using System.Threading.Tasks;
44
using JsonApiDotNetCore.Data;
5-
using JsonApiDotNetCore.Extensions;
65
using JsonApiDotNetCore.Models;
76
using Microsoft.EntityFrameworkCore;
87

Diff for: src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using JsonApiDotNetCore.Services;
2-
using Microsoft.AspNetCore.Http;
32
using System;
43

54
namespace JsonApiDotNetCore.Internal.Generics

0 commit comments

Comments
 (0)