Skip to content

Commit 8100643

Browse files
author
Bart Koelman
authored
Moved hooks into test project, refreshed example project (#1008)
1 parent 252113c commit 8100643

Some content is hidden

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

61 files changed

+608
-230
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using JsonApiDotNetCoreExample.Models;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExample.Controllers
8+
{
9+
public sealed class TagsController : JsonApiController<Tag>
10+
{
11+
public TagsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Tag> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}

src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ namespace JsonApiDotNetCoreExample.Data
1010
public sealed class AppDbContext : DbContext
1111
{
1212
public DbSet<TodoItem> TodoItems { get; set; }
13-
public DbSet<Person> People { get; set; }
14-
public DbSet<Article> Articles { get; set; }
15-
public DbSet<Author> AuthorDifferentDbContextName { get; set; }
16-
public DbSet<User> Users { get; set; }
1713

1814
public AppDbContext(DbContextOptions<AppDbContext> options)
1915
: base(options)
@@ -22,47 +18,26 @@ public AppDbContext(DbContextOptions<AppDbContext> options)
2218

2319
protected override void OnModelCreating(ModelBuilder builder)
2420
{
25-
builder.Entity<TodoItem>()
26-
.HasOne(todoItem => todoItem.Assignee)
27-
.WithMany(person => person.AssignedTodoItems);
28-
29-
builder.Entity<TodoItem>()
30-
.HasOne(todoItem => todoItem.Owner)
31-
.WithMany(person => person.TodoItems);
32-
33-
builder.Entity<ArticleTag>()
34-
.HasKey(bc => new
35-
{
36-
bc.ArticleId,
37-
bc.TagId
38-
});
39-
40-
builder.Entity<IdentifiableArticleTag>()
41-
.HasKey(bc => new
21+
builder.Entity<TodoItemTag>()
22+
.HasKey(todoItemTag => new
4223
{
43-
bc.ArticleId,
44-
bc.TagId
24+
todoItemTag.TodoItemId,
25+
todoItemTag.TagId
4526
});
4627

28+
// When deleting a person, un-assign him/her from existing todo items.
4729
builder.Entity<Person>()
48-
.HasOne(person => person.StakeHolderTodoItem)
49-
.WithMany(todoItem => todoItem.StakeHolders)
50-
.OnDelete(DeleteBehavior.Cascade);
51-
52-
builder.Entity<TodoItem>()
53-
.HasMany(todoItem => todoItem.ChildTodoItems)
54-
.WithOne(todoItem => todoItem.ParentTodo);
55-
56-
builder.Entity<Passport>()
57-
.HasOne(passport => passport.Person)
58-
.WithOne(person => person.Passport)
59-
.HasForeignKey<Person>("PassportKey")
30+
.HasMany(person => person.AssignedTodoItems)
31+
.WithOne(todoItem => todoItem.Assignee)
32+
.IsRequired(false)
6033
.OnDelete(DeleteBehavior.SetNull);
6134

35+
// When deleting a person, the todo items he/she owns are deleted too.
6236
builder.Entity<TodoItem>()
63-
.HasOne(todoItem => todoItem.OneToOnePerson)
64-
.WithOne(person => person.OneToOneTodoItem)
65-
.HasForeignKey<TodoItem>("OneToOnePersonKey");
37+
.HasOne(todoItem => todoItem.Owner)
38+
.WithMany()
39+
.IsRequired()
40+
.OnDelete(DeleteBehavior.Cascade);
6641
}
6742
}
6843
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.ComponentModel;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using JetBrains.Annotations;
5+
using JsonApiDotNetCore.Configuration;
6+
using JsonApiDotNetCore.Middleware;
7+
using JsonApiDotNetCore.Queries.Expressions;
8+
using JsonApiDotNetCore.Resources;
9+
using JsonApiDotNetCoreExample.Models;
10+
using Microsoft.AspNetCore.Authentication;
11+
12+
namespace JsonApiDotNetCoreExample.Definitions
13+
{
14+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
15+
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem>
16+
{
17+
private readonly ISystemClock _systemClock;
18+
19+
public TodoItemDefinition(IResourceGraph resourceGraph, ISystemClock systemClock)
20+
: base(resourceGraph)
21+
{
22+
_systemClock = systemClock;
23+
}
24+
25+
public override SortExpression OnApplySort(SortExpression existingSort)
26+
{
27+
return existingSort ?? GetDefaultSortOrder();
28+
}
29+
30+
private SortExpression GetDefaultSortOrder()
31+
{
32+
return CreateSortExpressionFromLambda(new PropertySortOrder
33+
{
34+
(todoItem => todoItem.Priority, ListSortDirection.Descending),
35+
(todoItem => todoItem.LastModifiedAt, ListSortDirection.Descending)
36+
});
37+
}
38+
39+
public override Task OnWritingAsync(TodoItem resource, OperationKind operationKind, CancellationToken cancellationToken)
40+
{
41+
if (operationKind == OperationKind.CreateResource)
42+
{
43+
resource.CreatedAt = _systemClock.UtcNow;
44+
}
45+
else if (operationKind == OperationKind.UpdateResource)
46+
{
47+
resource.LastModifiedAt = _systemClock.UtcNow;
48+
}
49+
50+
return Task.CompletedTask;
51+
}
52+
}
53+
}

src/Examples/JsonApiDotNetCoreExample/Models/Person.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,15 @@
66
namespace JsonApiDotNetCoreExample.Models
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class Person : Identifiable, IIsLockable
9+
public sealed class Person : Identifiable
1010
{
11-
public bool IsLocked { get; set; }
12-
1311
[Attr]
1412
public string FirstName { get; set; }
1513

1614
[Attr]
1715
public string LastName { get; set; }
1816

19-
[HasMany]
20-
public ISet<TodoItem> TodoItems { get; set; }
21-
2217
[HasMany]
2318
public ISet<TodoItem> AssignedTodoItems { get; set; }
24-
25-
[HasOne]
26-
public TodoItem OneToOneTodoItem { get; set; }
27-
28-
[HasOne]
29-
public TodoItem StakeHolderTodoItem { get; set; }
30-
31-
[HasOne]
32-
public Passport Passport { get; set; }
3319
}
3420
}

src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel.DataAnnotations;
12
using JetBrains.Annotations;
23
using JsonApiDotNetCore.Resources;
34
using JsonApiDotNetCore.Resources.Annotations;
@@ -7,6 +8,8 @@ namespace JsonApiDotNetCoreExample.Models
78
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
89
public sealed class Tag : Identifiable
910
{
11+
[Required]
12+
[MinLength(1)]
1013
[Attr]
1114
public string Name { get; set; }
1215
}
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations.Schema;
24
using JetBrains.Annotations;
35
using JsonApiDotNetCore.Resources;
46
using JsonApiDotNetCore.Resources.Annotations;
57

68
namespace JsonApiDotNetCoreExample.Models
79
{
810
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class TodoItem : Identifiable, IIsLockable
11+
public sealed class TodoItem : Identifiable
1012
{
11-
public bool IsLocked { get; set; }
12-
1313
[Attr]
1414
public string Description { get; set; }
1515

16+
[Attr]
17+
public TodoItemPriority Priority { get; set; }
18+
19+
[Attr(Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort | AttrCapabilities.AllowView)]
20+
public DateTimeOffset CreatedAt { get; set; }
21+
22+
[Attr(PublicName = "modifiedAt", Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort | AttrCapabilities.AllowView)]
23+
public DateTimeOffset? LastModifiedAt { get; set; }
24+
1625
[HasOne]
1726
public Person Owner { get; set; }
1827

1928
[HasOne]
2029
public Person Assignee { get; set; }
2130

22-
[HasOne]
23-
public Person OneToOnePerson { get; set; }
24-
25-
[HasMany]
26-
public ISet<Person> StakeHolders { get; set; }
27-
28-
// cyclical to-many structure
29-
[HasOne]
30-
public TodoItem ParentTodo { get; set; }
31+
[NotMapped]
32+
[HasManyThrough(nameof(TodoItemTags))]
33+
public ISet<Tag> Tags { get; set; }
3134

32-
[HasMany]
33-
public IList<TodoItem> ChildTodoItems { get; set; }
35+
public ISet<TodoItemTag> TodoItemTags { get; set; }
3436
}
3537
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using JetBrains.Annotations;
2+
3+
namespace JsonApiDotNetCoreExample.Models
4+
{
5+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
6+
public enum TodoItemPriority
7+
{
8+
Low,
9+
Medium,
10+
High
11+
}
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using JetBrains.Annotations;
2+
3+
namespace JsonApiDotNetCoreExample.Models
4+
{
5+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
6+
public sealed class TodoItemTag
7+
{
8+
public int TodoItemId { get; set; }
9+
public TodoItem TodoItem { get; set; }
10+
11+
public int TagId { get; set; }
12+
public Tag Tag { get; set; }
13+
}
14+
}

src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,27 @@ public override void ConfigureServices(IServiceCollection services)
2727
{
2828
services.AddSingleton<ISystemClock, SystemClock>();
2929

30-
services.AddDbContext<AppDbContext>(options => options.UseNpgsql(_connectionString));
31-
32-
services.AddJsonApi<AppDbContext>(ConfigureJsonApiOptions, discovery => discovery.AddCurrentAssembly());
33-
}
30+
services.AddDbContext<AppDbContext>(options =>
31+
{
32+
options.UseNpgsql(_connectionString);
33+
#if DEBUG
34+
options.EnableSensitiveDataLogging();
35+
options.EnableDetailedErrors();
36+
#endif
37+
});
3438

35-
private void ConfigureJsonApiOptions(JsonApiOptions options)
36-
{
37-
options.IncludeExceptionStackTraceInErrors = true;
38-
options.Namespace = "api/v1";
39-
options.DefaultPageSize = new PageSize(5);
40-
options.IncludeTotalResourceCount = true;
41-
options.ValidateModelState = true;
42-
options.SerializerSettings.Formatting = Formatting.Indented;
43-
options.SerializerSettings.Converters.Add(new StringEnumConverter());
39+
services.AddJsonApi<AppDbContext>(options =>
40+
{
41+
options.Namespace = "api/v1";
42+
options.UseRelativeLinks = true;
43+
options.ValidateModelState = true;
44+
options.IncludeTotalResourceCount = true;
45+
options.SerializerSettings.Formatting = Formatting.Indented;
46+
options.SerializerSettings.Converters.Add(new StringEnumConverter());
47+
#if DEBUG
48+
options.IncludeExceptionStackTraceInErrors = true;
49+
#endif
50+
}, discovery => discovery.AddCurrentAssembly());
4451
}
4552

4653
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ public void Can_add_resources_from_assembly_to_graph()
5959
// Assert
6060
IResourceGraph resourceGraph = _resourceGraphBuilder.Build();
6161

62-
ResourceContext personResource = resourceGraph.GetResourceContext(typeof(Person));
63-
personResource.Should().NotBeNull();
62+
ResourceContext personContext = resourceGraph.GetResourceContext(typeof(Person));
63+
personContext.Should().NotBeNull();
6464

65-
ResourceContext articleResource = resourceGraph.GetResourceContext(typeof(Article));
66-
articleResource.Should().NotBeNull();
65+
ResourceContext todoItemContext = resourceGraph.GetResourceContext(typeof(TodoItem));
66+
todoItemContext.Should().NotBeNull();
6767
}
6868

6969
[Fact]
@@ -79,8 +79,8 @@ public void Can_add_resource_from_current_assembly_to_graph()
7979
// Assert
8080
IResourceGraph resourceGraph = _resourceGraphBuilder.Build();
8181

82-
ResourceContext resource = resourceGraph.GetResourceContext(typeof(TestResource));
83-
resource.Should().NotBeNull();
82+
ResourceContext testContext = resourceGraph.GetResourceContext(typeof(TestResource));
83+
testContext.Should().NotBeNull();
8484
}
8585

8686
[Fact]

src/Examples/JsonApiDotNetCoreExample/Controllers/ArticlesController.cs renamed to test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/Controllers/ArticlesController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using JsonApiDotNetCore.Configuration;
22
using JsonApiDotNetCore.Controllers;
33
using JsonApiDotNetCore.Services;
4-
using JsonApiDotNetCoreExample.Models;
4+
using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Models;
55
using Microsoft.Extensions.Logging;
66

7-
namespace JsonApiDotNetCoreExample.Controllers
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Controllers
88
{
99
public sealed class ArticlesController : JsonApiController<Article>
1010
{

src/Examples/JsonApiDotNetCoreExample/Controllers/AuthorsController.cs renamed to test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/Controllers/AuthorsController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using JsonApiDotNetCore.Configuration;
22
using JsonApiDotNetCore.Controllers;
33
using JsonApiDotNetCore.Services;
4-
using JsonApiDotNetCoreExample.Models;
4+
using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Models;
55
using Microsoft.Extensions.Logging;
66

7-
namespace JsonApiDotNetCoreExample.Controllers
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Controllers
88
{
99
public sealed class AuthorsController : JsonApiController<Author>
1010
{

src/Examples/JsonApiDotNetCoreExample/Controllers/PassportsController.cs renamed to test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/Controllers/PassportsController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using JsonApiDotNetCore.Configuration;
22
using JsonApiDotNetCore.Controllers;
33
using JsonApiDotNetCore.Services;
4-
using JsonApiDotNetCoreExample.Models;
4+
using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Models;
55
using Microsoft.Extensions.Logging;
66

7-
namespace JsonApiDotNetCoreExample.Controllers
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Controllers
88
{
99
public sealed class PassportsController : JsonApiController<Passport>
1010
{
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Models;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ResourceHooks.Controllers
8+
{
9+
public sealed class PeopleController : JsonApiController<Person>
10+
{
11+
public PeopleController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Person> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}

0 commit comments

Comments
 (0)